Revert "Preliminaries to support better interaction of undo and recording"
This reverts commit6e75ae58ac
, reversing changes made to714d53e00f
.
This commit is contained in:
parent
950898dcb9
commit
4ef8da8f16
|
@ -1087,9 +1087,6 @@ bool AudacityApp::OnExceptionInMainLoop()
|
|||
// failed operation
|
||||
pProject->RollbackState();
|
||||
|
||||
// Forget pending changes in the TrackList
|
||||
pProject->GetTracks()->ClearPendingTracks();
|
||||
|
||||
pProject->RedrawProject();
|
||||
|
||||
// Give the user an alert
|
||||
|
|
|
@ -1947,7 +1947,6 @@ void AudacityProject::ModifyUndoMenuItems()
|
|||
mCommandManager.Modify(wxT("Undo"),
|
||||
wxString::Format(_("&Undo %s"),
|
||||
desc));
|
||||
mCommandManager.Enable(wxT("Undo"), this->UndoAvailable());
|
||||
}
|
||||
else {
|
||||
mCommandManager.Modify(wxT("Undo"),
|
||||
|
@ -1959,7 +1958,7 @@ void AudacityProject::ModifyUndoMenuItems()
|
|||
mCommandManager.Modify(wxT("Redo"),
|
||||
wxString::Format(_("&Redo %s"),
|
||||
desc));
|
||||
mCommandManager.Enable(wxT("Redo"), this->RedoAvailable());
|
||||
mCommandManager.Enable(wxT("Redo"), true);
|
||||
}
|
||||
else {
|
||||
mCommandManager.Modify(wxT("Redo"),
|
||||
|
@ -2139,10 +2138,10 @@ CommandFlag AudacityProject::GetUpdateFlags(bool checkActive)
|
|||
if (!mLastEffect.IsEmpty())
|
||||
flags |= HasLastEffectFlag;
|
||||
|
||||
if (UndoAvailable())
|
||||
if (GetUndoManager()->UndoAvailable())
|
||||
flags |= UndoAvailableFlag;
|
||||
|
||||
if (RedoAvailable())
|
||||
if (GetUndoManager()->RedoAvailable())
|
||||
flags |= RedoAvailableFlag;
|
||||
|
||||
if (ZoomInAvailable() && (flags & TracksExistFlag))
|
||||
|
@ -4795,7 +4794,7 @@ void AudacityProject::OnPrint(const CommandContext &)
|
|||
|
||||
void AudacityProject::OnUndo(const CommandContext &)
|
||||
{
|
||||
if (!UndoAvailable()) {
|
||||
if (!GetUndoManager()->UndoAvailable()) {
|
||||
AudacityMessageBox(_("Nothing to undo"));
|
||||
return;
|
||||
}
|
||||
|
@ -4825,7 +4824,7 @@ void AudacityProject::OnUndo(const CommandContext &)
|
|||
|
||||
void AudacityProject::OnRedo(const CommandContext &)
|
||||
{
|
||||
if (!RedoAvailable()) {
|
||||
if (!GetUndoManager()->RedoAvailable()) {
|
||||
AudacityMessageBox(_("Nothing to redo"));
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -210,11 +210,11 @@ double NoteTrack::GetEndTime() const
|
|||
return GetStartTime() + GetSeq().get_real_dur();
|
||||
}
|
||||
|
||||
void NoteTrack::DoSetHeight(int h)
|
||||
void NoteTrack::SetHeight(int h)
|
||||
{
|
||||
auto oldHeight = GetHeight();
|
||||
auto oldMargin = GetNoteMargin(oldHeight);
|
||||
PlayableTrack::DoSetHeight(h);
|
||||
Track::SetHeight(h);
|
||||
auto margin = GetNoteMargin(h);
|
||||
Zoom(
|
||||
wxRect{ 0, 0, 1, h }, // only height matters
|
||||
|
|
|
@ -84,7 +84,7 @@ class AUDACITY_DLL_API NoteTrack final
|
|||
double GetStartTime() const override;
|
||||
double GetEndTime() const override;
|
||||
|
||||
void DoSetHeight(int h) override;
|
||||
void SetHeight(int h) override;
|
||||
|
||||
Alg_seq &GetSeq() const;
|
||||
|
||||
|
|
|
@ -2019,6 +2019,25 @@ void AudacityProject::FixScrollbars()
|
|||
GetTrackPanel()->HandleCursorForPresentMouseState(); } );
|
||||
}
|
||||
|
||||
std::shared_ptr<Track> AudacityProject::GetFirstVisible()
|
||||
{
|
||||
std::shared_ptr<Track> pTrack;
|
||||
if (GetTracks()) {
|
||||
TrackListIterator iter(GetTracks());
|
||||
for (Track *t = iter.First(); t; t = iter.Next()) {
|
||||
int y = t->GetY();
|
||||
int h = t->GetHeight();
|
||||
if (y + h - 1 >= mViewInfo.vpos) {
|
||||
// At least the bottom row of pixels is not scrolled away above
|
||||
pTrack = Track::Pointer(t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pTrack;
|
||||
}
|
||||
|
||||
void AudacityProject::UpdateLayout()
|
||||
{
|
||||
if (!mTrackPanel)
|
||||
|
@ -3566,10 +3585,6 @@ XMLTagHandler *AudacityProject::HandleXMLChild(const wxChar *tag)
|
|||
return mTags.get();
|
||||
}
|
||||
|
||||
// Note that TrackList::Add includes assignment of unique in-session TrackId
|
||||
// to a reloaded track, though no promise that it equals the id it originally
|
||||
// had
|
||||
|
||||
if (!wxStrcmp(tag, wxT("wavetrack"))) {
|
||||
return mTracks->Add(mTrackFactory->NewWaveTrack());
|
||||
}
|
||||
|
@ -4460,20 +4475,6 @@ void AudacityProject::InitialState()
|
|||
this->UpdateMixerBoard();
|
||||
}
|
||||
|
||||
bool AudacityProject::UndoAvailable()
|
||||
{
|
||||
auto trackList = GetTracks();
|
||||
return GetUndoManager()->UndoAvailable() &&
|
||||
!GetTracks()->HasPendingChanges();
|
||||
}
|
||||
|
||||
bool AudacityProject::RedoAvailable()
|
||||
{
|
||||
auto trackList = GetTracks();
|
||||
return GetUndoManager()->RedoAvailable() &&
|
||||
!GetTracks()->HasPendingChanges();
|
||||
}
|
||||
|
||||
void AudacityProject::PushState(const wxString &desc, const wxString &shortDesc)
|
||||
{
|
||||
PushState(desc, shortDesc, UndoPush::AUTOSAVE);
|
||||
|
|
|
@ -192,6 +192,8 @@ class AUDACITY_DLL_API AudacityProject final : public wxFrame,
|
|||
const ViewInfo &GetViewInfo() const { return mViewInfo; }
|
||||
ViewInfo &GetViewInfo() { return mViewInfo; }
|
||||
|
||||
std::shared_ptr<Track> GetFirstVisible();
|
||||
|
||||
void GetPlayRegion(double* playRegionStart, double *playRegionEnd);
|
||||
bool IsPlayRegionLocked() { return mLockPlayRegion; }
|
||||
|
||||
|
@ -550,9 +552,6 @@ public:
|
|||
bool TryToMakeActionAllowed
|
||||
( CommandFlag & flags, CommandFlag flagsRqd, CommandFlag mask );
|
||||
|
||||
bool UndoAvailable();
|
||||
bool RedoAvailable();
|
||||
|
||||
void PushState(const wxString &desc, const wxString &shortDesc); // use UndoPush::AUTOSAVE
|
||||
void PushState(const wxString &desc, const wxString &shortDesc, UndoPush flags);
|
||||
void RollbackState();
|
||||
|
|
531
src/Track.cpp
531
src/Track.cpp
|
@ -82,8 +82,6 @@ Track::Track(const Track &orig)
|
|||
// Copy all the track properties except the actual contents
|
||||
void Track::Init(const Track &orig)
|
||||
{
|
||||
mId = orig.mId;
|
||||
|
||||
mDefaultName = orig.mDefaultName;
|
||||
mName = orig.mName;
|
||||
|
||||
|
@ -155,22 +153,6 @@ int Track::GetY() const
|
|||
}
|
||||
|
||||
void Track::SetY(int y)
|
||||
{
|
||||
auto pList = mList.lock();
|
||||
if (pList && !pList->mPendingUpdates.empty()) {
|
||||
auto orig = pList->FindById( GetId() );
|
||||
if (orig && orig != this) {
|
||||
// delegate, and rely on the update to copy back
|
||||
orig->SetY(y);
|
||||
pList->UpdatePendingTracks();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DoSetY(y);
|
||||
}
|
||||
|
||||
void Track::DoSetY(int y)
|
||||
{
|
||||
mY = y;
|
||||
}
|
||||
|
@ -186,29 +168,14 @@ int Track::GetHeight() const
|
|||
|
||||
void Track::SetHeight(int h)
|
||||
{
|
||||
mHeight = h;
|
||||
auto pList = mList.lock();
|
||||
if (pList && !pList->mPendingUpdates.empty()) {
|
||||
auto orig = pList->FindById( GetId() );
|
||||
if (orig && orig != this) {
|
||||
// delegate, and rely on RecalcPositions to copy back
|
||||
orig->SetHeight(h);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DoSetHeight(h);
|
||||
|
||||
if (pList) {
|
||||
pList->RecalcPositions(mNode);
|
||||
pList->ResizingEvent(mNode);
|
||||
}
|
||||
}
|
||||
|
||||
void Track::DoSetHeight(int h)
|
||||
{
|
||||
mHeight = h;
|
||||
}
|
||||
|
||||
bool Track::GetMinimized() const
|
||||
{
|
||||
return mMinimized;
|
||||
|
@ -217,53 +184,23 @@ bool Track::GetMinimized() const
|
|||
void Track::SetMinimized(bool isMinimized)
|
||||
{
|
||||
auto pList = mList.lock();
|
||||
if (pList && !pList->mPendingUpdates.empty()) {
|
||||
auto orig = pList->FindById( GetId() );
|
||||
if (orig && orig != this) {
|
||||
// delegate, and rely on RecalcPositions to copy back
|
||||
orig->SetMinimized(isMinimized);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DoSetMinimized(isMinimized);
|
||||
|
||||
mMinimized = isMinimized;
|
||||
if (pList) {
|
||||
pList->RecalcPositions(mNode);
|
||||
pList->ResizingEvent(mNode);
|
||||
}
|
||||
}
|
||||
|
||||
void Track::DoSetMinimized(bool isMinimized)
|
||||
{
|
||||
mMinimized = isMinimized;
|
||||
}
|
||||
|
||||
void Track::SetLinked(bool l)
|
||||
{
|
||||
auto pList = mList.lock();
|
||||
if (pList && !pList->mPendingUpdates.empty()) {
|
||||
auto orig = pList->FindById( GetId() );
|
||||
if (orig && orig != this) {
|
||||
// delegate, and rely on RecalcPositions to copy back
|
||||
orig->SetLinked(l);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DoSetLinked(l);
|
||||
|
||||
mLinked = l;
|
||||
if (pList) {
|
||||
pList->RecalcPositions(mNode);
|
||||
pList->ResizingEvent(mNode);
|
||||
}
|
||||
}
|
||||
|
||||
void Track::DoSetLinked(bool l)
|
||||
{
|
||||
mLinked = l;
|
||||
}
|
||||
|
||||
Track *Track::GetLink() const
|
||||
{
|
||||
auto pList = mList.lock();
|
||||
|
@ -272,16 +209,20 @@ Track *Track::GetLink() const
|
|||
|
||||
if (!pList->isNull(mNode)) {
|
||||
if (mLinked) {
|
||||
auto next = pList->getNext( mNode );
|
||||
if ( !pList->isNull( next ) )
|
||||
auto next = mNode;
|
||||
++next;
|
||||
if (!pList->isNull(next)) {
|
||||
return next->get();
|
||||
}
|
||||
}
|
||||
|
||||
auto prev = pList->getPrev( mNode );
|
||||
if ( !pList->isNull( prev ) ) {
|
||||
if (pList->hasPrev(mNode)) {
|
||||
auto prev = mNode;
|
||||
--prev;
|
||||
auto track = prev->get();
|
||||
if (track && track->GetLinked())
|
||||
if (track && track->GetLinked()) {
|
||||
return track;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -391,7 +332,7 @@ TrackListIterator::TrackListIterator(TrackList * val)
|
|||
, cur{}
|
||||
{
|
||||
if (l)
|
||||
cur = l->getBegin();
|
||||
cur = l->ListOfTracks::begin();
|
||||
}
|
||||
|
||||
Track *TrackListIterator::StartWith(Track * val)
|
||||
|
@ -421,13 +362,13 @@ Track *TrackListIterator::First(TrackList * val)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
cur = l->getBegin();
|
||||
cur = l->ListOfTracks::begin();
|
||||
|
||||
if (!l->isNull(cur)) {
|
||||
return cur->get();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Track *TrackListIterator::Last(bool skiplinked)
|
||||
|
@ -436,19 +377,18 @@ Track *TrackListIterator::Last(bool skiplinked)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
cur = l->getPrev( l->getEnd() );
|
||||
if ( l->isNull( cur ) )
|
||||
return nullptr;
|
||||
cur = l->ListOfTracks::end();
|
||||
if (l->hasPrev(cur))
|
||||
--cur;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
// With skiplinked set, we won't return the second channel of a linked pair
|
||||
if (skiplinked) {
|
||||
auto prev = l->getPrev( cur );
|
||||
if ( !l->isNull( prev ) &&
|
||||
!(*cur)->GetLinked() &&
|
||||
(*cur)->GetLink()
|
||||
)
|
||||
cur = prev;
|
||||
}
|
||||
if (skiplinked &&
|
||||
l->hasPrev(cur) &&
|
||||
!(*cur)->GetLinked() &&
|
||||
(*cur)->GetLink())
|
||||
--cur;
|
||||
|
||||
return cur->get();
|
||||
}
|
||||
|
@ -463,24 +403,27 @@ Track *TrackListIterator::Next(bool skipLinked)
|
|||
return nullptr;
|
||||
|
||||
if (skipLinked &&
|
||||
(*cur)->GetLinked())
|
||||
cur = l->getNext( cur );
|
||||
(*cur)->GetLinked()) {
|
||||
++cur;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TLI // if we are debugging this bit
|
||||
wxASSERT_MSG((!cur || (*l).Contains((*cur).t)), wxT("cur invalid after skipping linked tracks.")); // check that cur is in the list
|
||||
#endif
|
||||
|
||||
if (!l->isNull(cur))
|
||||
cur = l->getNext( cur );
|
||||
if (!l->isNull(cur)) {
|
||||
++cur;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TLI // if we are debugging this bit
|
||||
wxASSERT_MSG((!cur || (*l).Contains((*cur).t)), wxT("cur invalid after moving to next track.")); // check that cur is in the list if it is not null
|
||||
#endif
|
||||
|
||||
if (!l->isNull(cur))
|
||||
if (!l->isNull(cur)) {
|
||||
return cur->get();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Track *TrackListIterator::Prev(bool skiplinked)
|
||||
|
@ -488,13 +431,17 @@ Track *TrackListIterator::Prev(bool skiplinked)
|
|||
if (!l || l->isNull(cur))
|
||||
return nullptr;
|
||||
|
||||
cur = l->getPrev( cur );
|
||||
if ( l->isNull( cur ) )
|
||||
if (!l->hasPrev(cur)) {
|
||||
l->setNull(cur);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ( skiplinked ) {
|
||||
auto prev = l->getPrev( cur );
|
||||
if( !l->isNull( prev ) && (*prev)->GetLinked() )
|
||||
--cur;
|
||||
|
||||
if (skiplinked && l->hasPrev(cur)) {
|
||||
auto prev = cur;
|
||||
--prev;
|
||||
if ((*prev)->GetLinked())
|
||||
cur = prev;
|
||||
}
|
||||
|
||||
|
@ -511,19 +458,20 @@ Track *TrackListIterator::operator *() const
|
|||
|
||||
Track *TrackListIterator::RemoveCurrent()
|
||||
{
|
||||
if ( !l || l->isNull( cur ) )
|
||||
if (!l || l->isNull(cur))
|
||||
return nullptr;
|
||||
|
||||
cur = l->Remove( cur->get() );
|
||||
cur = l->Remove(cur->get());
|
||||
|
||||
#ifdef DEBUG_TLI // if we are debugging this bit
|
||||
wxASSERT_MSG((!cur || (*l).Contains((*cur).t)), wxT("cur invalid after deletion of track.")); // check that cur is in the list
|
||||
#endif
|
||||
|
||||
if ( !l->isNull( cur ) )
|
||||
if (!l->isNull(cur)) {
|
||||
return cur->get();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool TrackListIterator::operator == (const TrackListIterator &other) const
|
||||
|
@ -546,7 +494,7 @@ Track *TrackListCondIterator::StartWith(Track *val)
|
|||
Track *t = TrackListIterator::StartWith(val);
|
||||
|
||||
if (t && !this->Condition(t))
|
||||
return nullptr;
|
||||
return NULL;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
@ -707,11 +655,11 @@ Track *SyncLockedTracksIterator::Next(bool skiplinked)
|
|||
Track *t = TrackListIterator::Next(skiplinked);
|
||||
|
||||
if (!t)
|
||||
return nullptr;
|
||||
return NULL;
|
||||
|
||||
if ( ! IsGoodNextTrack(t) ) {
|
||||
cur = l->getEnd();
|
||||
return nullptr;
|
||||
l->setNull(cur);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mInLabelSection = ( t->GetKind() == Track::Label );
|
||||
|
@ -729,19 +677,19 @@ Track *SyncLockedTracksIterator::Prev(bool skiplinked)
|
|||
|
||||
// Beginning of tracks
|
||||
if (!t)
|
||||
return nullptr;
|
||||
return NULL;
|
||||
|
||||
const bool isLabel = ( t->GetKind() == Track::Label );
|
||||
const bool isSyncLockable = IsSyncLockableNonLabelTrack( t );
|
||||
|
||||
if ( !( isLabel || isSyncLockable ) ) {
|
||||
cur = l->getEnd();
|
||||
return nullptr;
|
||||
l->setNull(cur);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( !mInLabelSection && isLabel ) {
|
||||
cur = l->getEnd();
|
||||
return nullptr;
|
||||
l->setNull(cur);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mInLabelSection = isLabel;
|
||||
|
@ -751,8 +699,8 @@ Track *SyncLockedTracksIterator::Prev(bool skiplinked)
|
|||
|
||||
Track *SyncLockedTracksIterator::Last(bool skiplinked)
|
||||
{
|
||||
if ( !l || l->isNull( cur ) )
|
||||
return nullptr;
|
||||
if (!l || l->isNull(cur))
|
||||
return NULL;
|
||||
|
||||
Track *t = cur->get();
|
||||
|
||||
|
@ -776,9 +724,6 @@ DEFINE_EVENT_TYPE(EVT_TRACKLIST_PERMUTED);
|
|||
DEFINE_EVENT_TYPE(EVT_TRACKLIST_RESIZING);
|
||||
DEFINE_EVENT_TYPE(EVT_TRACKLIST_DELETION);
|
||||
|
||||
// same value as in the default constructed TrackId:
|
||||
long TrackList::mCounter = -1;
|
||||
|
||||
TrackList::TrackList()
|
||||
: wxEvtHandler()
|
||||
{
|
||||
|
@ -812,39 +757,20 @@ TrackList &TrackList::operator= (TrackList &&that)
|
|||
|
||||
void TrackList::DoAssign(const TrackList &that)
|
||||
{
|
||||
auto copyLOT = [](
|
||||
ListOfTracks &dst, const std::weak_ptr< TrackList > &self,
|
||||
const ListOfTracks &src )
|
||||
{
|
||||
for (const auto &ptr : src)
|
||||
dst.push_back(
|
||||
ListOfTracks::value_type{ ptr->Duplicate().release() } );
|
||||
for (auto it = dst.begin(), last = dst.end(); it != last; ++it)
|
||||
(*it)->SetOwner(self, it);
|
||||
};
|
||||
copyLOT( *this, mSelf, that );
|
||||
copyLOT( this->mPendingAdditions, mSelf, that.mPendingAdditions );
|
||||
copyLOT( this->mPendingUpdates, mSelf, that.mPendingUpdates );
|
||||
mUpdaters = that.mUpdaters;
|
||||
TrackListConstIterator it(&that);
|
||||
for (const Track *track = it.First(); track; track = it.Next())
|
||||
Add(track->Duplicate());
|
||||
}
|
||||
|
||||
void TrackList::Swap(TrackList &that)
|
||||
{
|
||||
auto SwapLOTs = [](
|
||||
ListOfTracks &a, const std::weak_ptr< TrackList > &aSelf,
|
||||
ListOfTracks &b, const std::weak_ptr< TrackList > &bSelf )
|
||||
{
|
||||
a.swap(b);
|
||||
for (auto it = a.begin(), last = a.end(); it != last; ++it)
|
||||
(*it)->SetOwner(aSelf, it);
|
||||
for (auto it = b.begin(), last = b.end(); it != last; ++it)
|
||||
(*it)->SetOwner(bSelf, it);
|
||||
};
|
||||
|
||||
SwapLOTs( *this, mSelf, that, that.mSelf );
|
||||
SwapLOTs( this->mPendingAdditions, mSelf, that.mPendingAdditions, that.mSelf );
|
||||
SwapLOTs( this->mPendingUpdates, mSelf, that.mPendingUpdates, that.mSelf );
|
||||
mUpdaters.swap(that.mUpdaters);
|
||||
ListOfTracks::swap(that);
|
||||
for (auto it = ListOfTracks::begin(), last = ListOfTracks::end();
|
||||
it != last; ++it)
|
||||
(*it)->SetOwner(this->mSelf, it);
|
||||
for (auto it = that.ListOfTracks::begin(), last = that.ListOfTracks::end();
|
||||
it != last; ++it)
|
||||
(*it)->SetOwner(that.mSelf, it);
|
||||
}
|
||||
|
||||
TrackList::~TrackList()
|
||||
|
@ -854,15 +780,16 @@ TrackList::~TrackList()
|
|||
|
||||
void TrackList::RecalcPositions(TrackNodePointer node)
|
||||
{
|
||||
if ( isNull( node ) )
|
||||
if (isNull(node)) {
|
||||
return;
|
||||
|
||||
}
|
||||
Track *t;
|
||||
int i = 0;
|
||||
int y = 0;
|
||||
|
||||
auto prev = getPrev( node );
|
||||
if ( !isNull( prev ) ) {
|
||||
if (hasPrev(node)) {
|
||||
auto prev = node;
|
||||
--prev;
|
||||
t = prev->get();
|
||||
i = t->GetIndex() + 1;
|
||||
y = t->GetY() + t->GetHeight();
|
||||
|
@ -872,11 +799,9 @@ void TrackList::RecalcPositions(TrackNodePointer node)
|
|||
for (auto n = TrackListIterator{ this, node }; n != theEnd; ++n) {
|
||||
t = *n;
|
||||
t->SetIndex(i++);
|
||||
t->DoSetY(y);
|
||||
t->SetY(y);
|
||||
y += t->GetHeight();
|
||||
}
|
||||
|
||||
UpdatePendingTracks();
|
||||
}
|
||||
|
||||
void TrackList::PermutationEvent()
|
||||
|
@ -909,32 +834,19 @@ void TrackList::Permute(const std::vector<TrackNodePointer> &permutation)
|
|||
Track *pTrack = track.get();
|
||||
pTrack->SetOwner(mSelf, insert(ListOfTracks::end(), std::move(track)));
|
||||
}
|
||||
auto n = getBegin();
|
||||
auto n = ListOfTracks::begin();
|
||||
RecalcPositions(n);
|
||||
PermutationEvent();
|
||||
}
|
||||
|
||||
Track *TrackList::FindById( TrackId id )
|
||||
{
|
||||
// Linear search. Tracks in a project are usually very few.
|
||||
// Search only the non-pending tracks.
|
||||
auto it = std::find_if( ListOfTracks::begin(), ListOfTracks::end(),
|
||||
[=](const ListOfTracks::value_type &ptr){ return ptr->GetId() == id; } );
|
||||
if (it == ListOfTracks::end())
|
||||
return {};
|
||||
return it->get();
|
||||
}
|
||||
|
||||
template<typename TrackKind>
|
||||
Track *TrackList::Add(std::unique_ptr<TrackKind> &&t)
|
||||
{
|
||||
Track *pTrack;
|
||||
push_back(ListOfTracks::value_type(pTrack = t.release()));
|
||||
|
||||
auto n = getPrev( getEnd() );
|
||||
|
||||
auto n = ListOfTracks::end();
|
||||
--n;
|
||||
pTrack->SetOwner(mSelf, n);
|
||||
pTrack->SetId( TrackId{ ++mCounter } );
|
||||
RecalcPositions(n);
|
||||
ResizingEvent(n);
|
||||
return back().get();
|
||||
|
@ -954,9 +866,8 @@ Track *TrackList::AddToHead(std::unique_ptr<TrackKind> &&t)
|
|||
{
|
||||
Track *pTrack;
|
||||
push_front(ListOfTracks::value_type(pTrack = t.release()));
|
||||
auto n = getBegin();
|
||||
auto n = ListOfTracks::begin();
|
||||
pTrack->SetOwner(mSelf, n);
|
||||
pTrack->SetId( TrackId{ ++mCounter } );
|
||||
RecalcPositions(n);
|
||||
ResizingEvent(n);
|
||||
return front().get();
|
||||
|
@ -969,11 +880,9 @@ template<typename TrackKind>
|
|||
Track *TrackList::Add(std::shared_ptr<TrackKind> &&t)
|
||||
{
|
||||
push_back(t);
|
||||
|
||||
auto n = getPrev( getEnd() );
|
||||
|
||||
auto n = ListOfTracks::end();
|
||||
--n;
|
||||
t->SetOwner(mSelf, n);
|
||||
t->SetId( TrackId{ ++mCounter } );
|
||||
RecalcPositions(n);
|
||||
ResizingEvent(n);
|
||||
return back().get();
|
||||
|
@ -996,7 +905,6 @@ auto TrackList::Replace(Track * t, ListOfTracks::value_type &&with) ->
|
|||
Track *pTrack = with.get();
|
||||
*node = std::move(with);
|
||||
pTrack->SetOwner(mSelf, node);
|
||||
pTrack->SetId( t->GetId() );
|
||||
RecalcPositions(node);
|
||||
|
||||
DeletionEvent();
|
||||
|
@ -1007,18 +915,18 @@ auto TrackList::Replace(Track * t, ListOfTracks::value_type &&with) ->
|
|||
|
||||
TrackNodePointer TrackList::Remove(Track *t)
|
||||
{
|
||||
auto result = getEnd();
|
||||
auto result = ListOfTracks::end();
|
||||
if (t) {
|
||||
auto node = t->GetNode();
|
||||
t->SetOwner({}, {});
|
||||
|
||||
if ( !isNull( node ) ) {
|
||||
if (!isNull(node)) {
|
||||
ListOfTracks::value_type holder = std::move( *node );
|
||||
|
||||
result = getNext( node );
|
||||
erase(node);
|
||||
if ( !isNull( result ) )
|
||||
result = erase(node);
|
||||
if (!isNull(result)) {
|
||||
RecalcPositions(result);
|
||||
}
|
||||
|
||||
DeletionEvent();
|
||||
}
|
||||
|
@ -1032,22 +940,9 @@ void TrackList::Clear(bool sendEvent)
|
|||
// shared_ptrs to those tracks.
|
||||
for ( auto pTrack: *this )
|
||||
pTrack->SetOwner( {}, {} );
|
||||
for ( auto pTrack: mPendingAdditions )
|
||||
pTrack->SetOwner( {}, {} );
|
||||
for ( auto pTrack: mPendingAdditions )
|
||||
pTrack->SetOwner( {}, {} );
|
||||
|
||||
ListOfTracks tempList;
|
||||
tempList.swap( *this );
|
||||
|
||||
ListOfTracks additional;
|
||||
additional.swap( mPendingAdditions );
|
||||
|
||||
ListOfTracks updating;
|
||||
updating.swap( mPendingAdditions );
|
||||
|
||||
mUpdaters.clear();
|
||||
|
||||
if (sendEvent)
|
||||
DeletionEvent();
|
||||
}
|
||||
|
@ -1056,17 +951,19 @@ void TrackList::Select(Track * t, bool selected /* = true */ )
|
|||
{
|
||||
if (t) {
|
||||
const auto node = t->GetNode();
|
||||
if ( !isNull( node ) ) {
|
||||
t->SetSelected( selected );
|
||||
if ( t->GetLinked() ) {
|
||||
auto next = getNext( node );
|
||||
if ( !isNull( next ) )
|
||||
(*next)->SetSelected( selected );
|
||||
if (!isNull(node)) {
|
||||
t->SetSelected(selected);
|
||||
auto next = node;
|
||||
++next;
|
||||
if (t->GetLinked() && !isNull(next)) {
|
||||
(*next)->SetSelected(selected);
|
||||
}
|
||||
else {
|
||||
auto prev = getPrev( node );
|
||||
if ( !isNull( prev ) && (*prev)->GetLinked() )
|
||||
(*prev)->SetSelected( selected );
|
||||
else if (hasPrev(node)) {
|
||||
auto prev = node;
|
||||
--prev;
|
||||
if ((*prev)->GetLinked()) {
|
||||
(*prev)->SetSelected(selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1077,55 +974,50 @@ Track *TrackList::GetNext(Track * t, bool linked) const
|
|||
{
|
||||
if (t) {
|
||||
auto node = t->GetNode();
|
||||
if ( !isNull( node ) ) {
|
||||
if ( linked && t->GetLinked() )
|
||||
node = getNext( node );
|
||||
if (!isNull(node)) {
|
||||
if (linked && t->GetLinked()) {
|
||||
++node;
|
||||
}
|
||||
|
||||
if ( !isNull( node ) )
|
||||
node = getNext( node );
|
||||
if (!isNull(node)) {
|
||||
++node;
|
||||
}
|
||||
|
||||
if ( !isNull( node ) )
|
||||
if (!isNull(node)) {
|
||||
return node->get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Track *TrackList::GetPrev(Track * t, bool linked) const
|
||||
{
|
||||
if (t) {
|
||||
TrackNodePointer prev;
|
||||
auto node = t->GetNode();
|
||||
if ( !isNull( node ) ) {
|
||||
if (!isNull(node)) {
|
||||
// linked is true and input track second in team?
|
||||
if (linked) {
|
||||
prev = getPrev( node );
|
||||
if( !isNull( prev ) &&
|
||||
!t->GetLinked() && t->GetLink() )
|
||||
if (linked && hasPrev(node) &&
|
||||
!t->GetLinked() && t->GetLink())
|
||||
// Make it the first
|
||||
node = prev;
|
||||
}
|
||||
--node;
|
||||
|
||||
prev = getPrev( node );
|
||||
if ( !isNull( prev ) ) {
|
||||
if (hasPrev(node)) {
|
||||
// Back up once
|
||||
node = prev;
|
||||
--node;
|
||||
|
||||
// Back up twice sometimes when linked is true
|
||||
if (linked) {
|
||||
prev = getPrev( node );
|
||||
if( !isNull( prev ) &&
|
||||
!(*node)->GetLinked() && (*node)->GetLink() )
|
||||
node = prev;
|
||||
}
|
||||
if (linked && hasPrev(node) &&
|
||||
!(*node)->GetLinked() && (*node)->GetLink())
|
||||
--node;
|
||||
|
||||
return node->get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// For mono track height of track
|
||||
|
@ -1260,7 +1152,7 @@ size_t TrackList::size() const
|
|||
int cnt = 0;
|
||||
|
||||
if (!empty())
|
||||
cnt = getPrev( getEnd() )->get()->GetIndex() + 1;
|
||||
cnt = back()->GetIndex() + 1;
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
@ -1398,7 +1290,7 @@ int TrackList::GetHeight() const
|
|||
int height = 0;
|
||||
|
||||
if (!empty()) {
|
||||
auto track = getPrev( getEnd() )->get();
|
||||
const auto &track = back();
|
||||
height = track->GetY() + track->GetHeight();
|
||||
}
|
||||
|
||||
|
@ -1443,186 +1335,3 @@ double TrackList::GetEndTime() const
|
|||
{
|
||||
return Accumulate(*this, &Track::GetEndTime, doubleMax);
|
||||
}
|
||||
|
||||
std::shared_ptr<Track>
|
||||
TrackList::RegisterPendingChangedTrack( Updater updater, Track *src )
|
||||
{
|
||||
std::shared_ptr<Track> pTrack;
|
||||
if (src)
|
||||
// convert from unique_ptr to shared_ptr
|
||||
pTrack.reset( src->Duplicate().release() );
|
||||
|
||||
if (pTrack) {
|
||||
mUpdaters.push_back( updater );
|
||||
mPendingUpdates.push_back( pTrack );
|
||||
auto n = mPendingUpdates.end();
|
||||
--n;
|
||||
pTrack->SetOwner(mSelf, n);
|
||||
}
|
||||
|
||||
return pTrack;
|
||||
}
|
||||
|
||||
void TrackList::RegisterPendingNewTrack( const std::shared_ptr<Track> &pTrack )
|
||||
{
|
||||
pTrack->SetIndex( size() );
|
||||
pTrack->SetY( GetHeight() );
|
||||
|
||||
mPendingAdditions.push_back(pTrack);
|
||||
pTrack->SetOwner(mSelf, getPrev( getEnd() ));
|
||||
}
|
||||
|
||||
void TrackList::UpdatePendingTracks()
|
||||
{
|
||||
auto pUpdater = mUpdaters.begin();
|
||||
for (const auto &pendingTrack : mPendingUpdates) {
|
||||
// Copy just a part of the track state, according to the update
|
||||
// function
|
||||
const auto &updater = *pUpdater;
|
||||
auto src = FindById( pendingTrack->GetId() );
|
||||
if (pendingTrack && src) {
|
||||
if (updater)
|
||||
updater( *pendingTrack, *src );
|
||||
pendingTrack->DoSetY(src->GetY());
|
||||
pendingTrack->DoSetHeight(src->GetHeight());
|
||||
pendingTrack->DoSetMinimized(src->GetMinimized());
|
||||
pendingTrack->DoSetLinked(src->GetLinked());
|
||||
}
|
||||
++pUpdater;
|
||||
}
|
||||
}
|
||||
|
||||
void TrackList::ClearPendingTracks()
|
||||
// NOFAIL-GUARANTEE
|
||||
{
|
||||
for (const auto &pTrack: mPendingUpdates)
|
||||
pTrack->SetOwner( {}, {} );
|
||||
|
||||
for (const auto &pTrack: mPendingAdditions)
|
||||
pTrack->SetOwner( {}, {} );
|
||||
|
||||
mPendingAdditions.clear();
|
||||
mPendingUpdates.clear();
|
||||
mUpdaters.clear();
|
||||
}
|
||||
|
||||
bool TrackList::ApplyPendingTracks()
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
// Always clear, even if one of the update functions throws
|
||||
auto cleanup = finally( [this] { ClearPendingTracks(); } );
|
||||
|
||||
UpdatePendingTracks();
|
||||
|
||||
// Remaining steps must be NOFAIL-GUARANTEE so that this function
|
||||
// gives STRONG-GUARANTEE
|
||||
|
||||
std::vector< std::shared_ptr<Track> > reinstated;
|
||||
|
||||
ListOfTracks updates;
|
||||
updates.swap( mPendingUpdates );
|
||||
ListOfTracks additions;
|
||||
additions.swap( mPendingAdditions );
|
||||
|
||||
for (auto &pendingTrack : updates) {
|
||||
if (pendingTrack) {
|
||||
auto src = FindById( pendingTrack->GetId() );
|
||||
if (src)
|
||||
this->Replace(src, std::move(pendingTrack)), result = true;
|
||||
else
|
||||
// Perhaps a track marked for pending changes got deleted by
|
||||
// some other action. Recreate it so we don't lose the
|
||||
// accumulated changes.
|
||||
reinstated.push_back(pendingTrack);
|
||||
}
|
||||
}
|
||||
|
||||
// If there are tracks to reinstate, append them to the list before the
|
||||
// additional tracks.
|
||||
for (auto &pendingTrack : reinstated)
|
||||
if (pendingTrack)
|
||||
this->Add(std::move(pendingTrack)), result = true;
|
||||
|
||||
for (auto &pendingTrack : additions)
|
||||
if (pendingTrack)
|
||||
this->Add(std::move(pendingTrack)), result = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<Track> TrackList::FindPendingChangedTrack(TrackId id) const
|
||||
{
|
||||
// Linear search. Tracks in a project are usually very few.
|
||||
auto it = std::find_if( mPendingUpdates.begin(), mPendingUpdates.end(),
|
||||
[=](const ListOfTracks::value_type &ptr){ return ptr->GetId() == id; } );
|
||||
if (it == mPendingUpdates.end())
|
||||
return {};
|
||||
return *it;
|
||||
}
|
||||
|
||||
// Make begin iterator
|
||||
PendingTrackIterator::PendingTrackIterator
|
||||
(TrackList *list, const std::shared_ptr<TrackListIterator> &pIter)
|
||||
: mList{ list }
|
||||
, mpIter{ pIter }
|
||||
{
|
||||
auto &pending = mList->FindPendingNewTracks();
|
||||
|
||||
// Assume no invalidation of these iterators happens during the lifetime
|
||||
// of this!
|
||||
mpPendingIt = pending.begin(), mpPendingEnd = pending.end();
|
||||
|
||||
mpTrack = Track::Pointer( mpIter->First() );
|
||||
SubstituteTrack();
|
||||
|
||||
mDoingExtras = !mpTrack;
|
||||
FindExtraTrack();
|
||||
}
|
||||
|
||||
// Make end iterator; the only important thing is to define operators == and
|
||||
// != with other iterators
|
||||
PendingTrackIterator::PendingTrackIterator()
|
||||
: mList{ nullptr }
|
||||
, mpIter{ }
|
||||
{
|
||||
mDoingExtras = true;
|
||||
}
|
||||
|
||||
PendingTrackIterator &PendingTrackIterator::operator++()
|
||||
{
|
||||
if (!mDoingExtras) {
|
||||
Track *next = mpIter->Next();
|
||||
if (next) {
|
||||
mpTrack = Track::Pointer( next );
|
||||
SubstituteTrack();
|
||||
}
|
||||
else
|
||||
mDoingExtras = true;
|
||||
}
|
||||
FindExtraTrack();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void PendingTrackIterator::SubstituteTrack()
|
||||
{
|
||||
if (mpTrack) {
|
||||
auto sub = mList->FindPendingChangedTrack( mpTrack->GetId() );
|
||||
if (sub)
|
||||
mpTrack = sub;
|
||||
}
|
||||
}
|
||||
|
||||
void PendingTrackIterator::FindExtraTrack()
|
||||
{
|
||||
if (mDoingExtras) {
|
||||
auto pCondIter = dynamic_cast<TrackListCondIterator*>(mpIter.get());
|
||||
while(
|
||||
mpTrack.reset(),
|
||||
mpPendingIt != mpPendingEnd &&
|
||||
(mpTrack = *mpPendingIt++,
|
||||
pCondIter &&
|
||||
!pCondIter->Condition(mpTrack.get())))
|
||||
{}
|
||||
}
|
||||
}
|
||||
|
|
230
src/Track.h
230
src/Track.h
|
@ -16,7 +16,6 @@
|
|||
#include "MemoryX.h"
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <functional>
|
||||
#include <wx/dynarray.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/gdicmn.h>
|
||||
|
@ -65,33 +64,6 @@ using TrackNodePointer = ListOfTracks::iterator;
|
|||
|
||||
class ViewInfo;
|
||||
|
||||
// This is an in-session identifier of track objects across undo states
|
||||
// It does not persist between sessions
|
||||
// Default constructed value is not equal to the id of any track that has ever
|
||||
// been added to a TrackList, or (directly or transitively) copied from such
|
||||
// (A pending additional track that is not yet applied is not considered added)
|
||||
// TrackIds are assigned uniquely across projects
|
||||
class TrackId
|
||||
{
|
||||
public:
|
||||
TrackId() : mValue(-1) {}
|
||||
explicit TrackId (long value) : mValue(value) {}
|
||||
|
||||
bool operator == (const TrackId &other) const
|
||||
{ return mValue == other.mValue; }
|
||||
|
||||
bool operator != (const TrackId &other) const
|
||||
{ return mValue != other.mValue; }
|
||||
|
||||
// Define this in case you want to key a std::map on TrackId
|
||||
// The operator does not mean anything else
|
||||
bool operator < (const TrackId &other) const
|
||||
{ return mValue < other.mValue; }
|
||||
|
||||
private:
|
||||
long mValue;
|
||||
};
|
||||
|
||||
class AUDACITY_DLL_API Track /* not final */
|
||||
: public CommonTrackPanelCell, public XMLTagHandler
|
||||
{
|
||||
|
@ -100,9 +72,6 @@ class AUDACITY_DLL_API Track /* not final */
|
|||
friend class SyncLockedTracksIterator;
|
||||
|
||||
// To be TrackDisplay
|
||||
private:
|
||||
TrackId mId;
|
||||
|
||||
protected:
|
||||
std::weak_ptr<TrackList> mList;
|
||||
TrackNodePointer mNode{};
|
||||
|
@ -117,11 +86,6 @@ class AUDACITY_DLL_API Track /* not final */
|
|||
bool mLinked;
|
||||
bool mMinimized;
|
||||
|
||||
public:
|
||||
|
||||
TrackId GetId() const { return mId; }
|
||||
private:
|
||||
void SetId( TrackId id ) { mId = id; }
|
||||
public:
|
||||
|
||||
// Given a bare pointer, find a shared_ptr. But this is not possible for
|
||||
|
@ -187,28 +151,12 @@ class AUDACITY_DLL_API Track /* not final */
|
|||
|
||||
int GetIndex() const;
|
||||
void SetIndex(int index);
|
||||
|
||||
int GetY() const;
|
||||
private:
|
||||
// Always maintain a strictly contiguous layout of tracks.
|
||||
// So client code is not permitted to modify this attribute directly.
|
||||
void SetY(int y);
|
||||
// No need yet to make this virtual
|
||||
void DoSetY(int y);
|
||||
public:
|
||||
|
||||
int GetHeight() const;
|
||||
void SetHeight(int h);
|
||||
protected:
|
||||
virtual void DoSetHeight(int h);
|
||||
public:
|
||||
|
||||
virtual void SetHeight(int h);
|
||||
bool GetMinimized() const;
|
||||
void SetMinimized(bool isMinimized);
|
||||
protected:
|
||||
virtual void DoSetMinimized(bool isMinimized);
|
||||
public:
|
||||
|
||||
virtual void SetMinimized(bool isMinimized);
|
||||
Track *GetLink() const;
|
||||
|
||||
private:
|
||||
|
@ -267,14 +215,10 @@ public:
|
|||
void SetDefaultName( const wxString &n ) { mDefaultName = n; }
|
||||
|
||||
bool GetSelected() const { return mSelected; }
|
||||
virtual void SetSelected(bool s);
|
||||
|
||||
bool GetLinked () const { return mLinked; }
|
||||
|
||||
virtual void SetSelected(bool s);
|
||||
void SetLinked (bool l);
|
||||
private:
|
||||
// No need yet to make this virtual
|
||||
void DoSetLinked(bool l);
|
||||
public:
|
||||
|
||||
virtual int GetChannel() const { return mChannel;};
|
||||
virtual double GetOffset() const = 0;
|
||||
|
@ -501,6 +445,7 @@ class AUDACITY_DLL_API TrackListCondIterator /* not final */ : public TrackListI
|
|||
Track *Prev(bool skiplinked = false) override;
|
||||
Track *Last(bool skiplinked = false) override;
|
||||
|
||||
protected:
|
||||
// NEW virtual
|
||||
virtual bool Condition(Track *t) = 0;
|
||||
};
|
||||
|
@ -578,52 +523,6 @@ class AUDACITY_DLL_API SyncLockedTracksIterator final : public TrackListIterator
|
|||
};
|
||||
|
||||
|
||||
// Iterates over the tracks, substituting any pending replacement tracks
|
||||
// for the actual, and always appending the added tracks, but then
|
||||
// also filtering all tracks according to the criterion in the
|
||||
// TrackListIterator given to the constructor.
|
||||
// Iterator may be invalidated if the project's TrackList is mutated during
|
||||
// the iterator's lifetime (though not in case of RegisterPendingChangedTrack()).
|
||||
class PendingTrackIterator {
|
||||
public:
|
||||
// Construct a begin iterator
|
||||
PendingTrackIterator
|
||||
(TrackList *list,
|
||||
// Track iterator that can include a condition
|
||||
const std::shared_ptr<TrackListIterator> &pIter);
|
||||
|
||||
// Construct an end iterator
|
||||
PendingTrackIterator();
|
||||
|
||||
PendingTrackIterator &operator++();
|
||||
|
||||
const std::shared_ptr<Track> &operator* () const { return mpTrack; }
|
||||
|
||||
bool operator == (const PendingTrackIterator &other) const
|
||||
// Test for identity of the tracks pointed to should be a sufficient
|
||||
// test of iterator equality, assuming no track is ever duplicated in the
|
||||
// sequence, which should be the case, and that an end iterator stores a
|
||||
// null pointer.
|
||||
{ return mpTrack == other.mpTrack; }
|
||||
|
||||
bool operator != (const PendingTrackIterator &other) const
|
||||
{ return !(*this == other); }
|
||||
|
||||
protected:
|
||||
|
||||
std::shared_ptr<Track> mpTrack;
|
||||
|
||||
private:
|
||||
void SubstituteTrack();
|
||||
void FindExtraTrack();
|
||||
|
||||
TrackList *mList;
|
||||
std::shared_ptr<TrackListIterator> mpIter;
|
||||
|
||||
bool mDoingExtras{ false };
|
||||
ListOfTracks::const_iterator mpPendingIt, mpPendingEnd;
|
||||
};
|
||||
|
||||
/** \brief TrackList is a flat linked list of tracks supporting Add, Remove,
|
||||
* Clear, and Contains, plus serialization of the list of tracks.
|
||||
*/
|
||||
|
@ -698,22 +597,16 @@ class TrackList final : public wxEvtHandler, public ListOfTracks
|
|||
/// For use in sorting: assume each iterator points into this list, no duplications
|
||||
void Permute(const std::vector<TrackNodePointer> &permutation);
|
||||
|
||||
Track *FindById( TrackId id );
|
||||
|
||||
/// Add a Track, giving it a fresh id
|
||||
/// Add this Track or all children of this TrackList.
|
||||
template<typename TrackKind>
|
||||
Track *Add(std::unique_ptr<TrackKind> &&t);
|
||||
|
||||
/// Add a Track, giving it a fresh id
|
||||
template<typename TrackKind>
|
||||
Track *AddToHead(std::unique_ptr<TrackKind> &&t);
|
||||
|
||||
/// Add a Track, giving it a fresh id
|
||||
template<typename TrackKind>
|
||||
Track *Add(std::shared_ptr<TrackKind> &&t);
|
||||
|
||||
/// Replace first track with second track, give back a holder
|
||||
/// Give the replacement the same id as the replaced
|
||||
ListOfTracks::value_type Replace(Track * t, ListOfTracks::value_type &&with);
|
||||
|
||||
/// Remove this Track or all children of this TrackList.
|
||||
|
@ -794,42 +687,11 @@ class TrackList final : public wxEvtHandler, public ListOfTracks
|
|||
|
||||
private:
|
||||
bool isNull(TrackNodePointer p) const
|
||||
{ return p == ListOfTracks::end()
|
||||
|| p == mPendingUpdates.end()
|
||||
|| p == mPendingAdditions.end(); }
|
||||
TrackNodePointer getEnd() const
|
||||
{ return const_cast<TrackList*>(this)->mPendingAdditions.end(); }
|
||||
TrackNodePointer getBegin() const {
|
||||
auto p = const_cast<TrackList*>(this)->ListOfTracks::begin();
|
||||
if ( p == this->ListOfTracks::end() )
|
||||
p = const_cast<TrackList*>(this)->mPendingAdditions.begin();
|
||||
return p;
|
||||
}
|
||||
|
||||
// Move an iterator to the next node, if any; else stay at end
|
||||
TrackNodePointer getNext(TrackNodePointer p) const
|
||||
{
|
||||
if (p == mPendingAdditions.end())
|
||||
return p;
|
||||
auto q = p;
|
||||
++q;
|
||||
if (q == this->ListOfTracks::end())
|
||||
return const_cast<TrackList*>(this)->mPendingAdditions.begin();
|
||||
return q;
|
||||
}
|
||||
|
||||
// Move an iterator to the previous node, if any; else wrap to end
|
||||
TrackNodePointer getPrev(TrackNodePointer p) const
|
||||
{
|
||||
if (p == mPendingAdditions.begin())
|
||||
p = const_cast<TrackList*>(this)->ListOfTracks::end();
|
||||
if (p == this->ListOfTracks::begin())
|
||||
return getEnd();
|
||||
else {
|
||||
auto q = p;
|
||||
return --q;
|
||||
}
|
||||
}
|
||||
{ return p == ListOfTracks::end(); }
|
||||
void setNull(TrackNodePointer &p)
|
||||
{ p = ListOfTracks::end(); }
|
||||
bool hasPrev(TrackNodePointer p) const
|
||||
{ return p != ListOfTracks::begin(); }
|
||||
|
||||
void DoAssign(const TrackList &that);
|
||||
|
||||
|
@ -841,76 +703,6 @@ private:
|
|||
void SwapNodes(TrackNodePointer s1, TrackNodePointer s2);
|
||||
|
||||
std::weak_ptr<TrackList> mSelf;
|
||||
|
||||
// Nondecreasing during the session.
|
||||
// Nonpersistent.
|
||||
// Used to assign ids to added tracks.
|
||||
static long mCounter;
|
||||
|
||||
public:
|
||||
using Updater = std::function< void(Track &dest, const Track &src) >;
|
||||
// Start a deferred update of the project.
|
||||
// The return value is a duplicate of the given track.
|
||||
// While ApplyPendingTracks or ClearPendingTracks is not yet called,
|
||||
// there may be other direct changes to the project that push undo history.
|
||||
// Meanwhile the returned object can accumulate other changes for a deferred
|
||||
// push, and temporarily shadow the actual project track for display purposes.
|
||||
// The Updater function, if not null, merges state (from the actual project
|
||||
// into the pending track) which is not meant to be overridden by the
|
||||
// accumulated pending changes.
|
||||
// To keep the display consistent, the Y and Height values, minimized state,
|
||||
// and Linked state must be copied, and this will be done even if the
|
||||
// Updater does not do it.
|
||||
// Pending track will have the same TrackId as the actual.
|
||||
// Pending changed tracks will not occur in iterations.
|
||||
std::shared_ptr<Track> RegisterPendingChangedTrack(
|
||||
Updater updater,
|
||||
Track *src
|
||||
);
|
||||
|
||||
// Like the previous, but for a NEW track, not a replacement track. Caller
|
||||
// supplies the track, and there are no updates.
|
||||
// Pending track will have an unassigned TrackId.
|
||||
// Pending changed tracks WILL occur in iterations, always after actual
|
||||
// tracks, and in the sequence that they were added. They can be
|
||||
// distinguished from actual tracks by TrackId.
|
||||
void RegisterPendingNewTrack( const std::shared_ptr<Track> &pTrack );
|
||||
|
||||
// Invoke the updaters of pending tracks. Pass any exceptions from the
|
||||
// updater functions.
|
||||
void UpdatePendingTracks();
|
||||
|
||||
// Forget pending track additions and changes.
|
||||
void ClearPendingTracks();
|
||||
|
||||
// Change the state of the project.
|
||||
// Strong guarantee for project state in case of exceptions.
|
||||
// Will always clear the pending updates.
|
||||
// Return true if the state of the track list really did change.
|
||||
bool ApplyPendingTracks();
|
||||
|
||||
// Find anything registered with RegisterPendingChangedTrack and not yet
|
||||
// cleared or applied
|
||||
std::shared_ptr<Track> FindPendingChangedTrack(TrackId id) const;
|
||||
|
||||
// List tracks given to RegisterPendingChangedTrack and not yet cleared or
|
||||
// applied, in the same sequence as they were added
|
||||
const ListOfTracks &FindPendingNewTracks() const
|
||||
{
|
||||
return mPendingAdditions;
|
||||
}
|
||||
|
||||
bool HasPendingChanges() const
|
||||
{
|
||||
return !( mPendingUpdates.empty() && mPendingAdditions.empty() );
|
||||
}
|
||||
|
||||
private:
|
||||
// Need to put pending tracks into lists so that GetLink() works
|
||||
ListOfTracks mPendingAdditions;
|
||||
ListOfTracks mPendingUpdates;
|
||||
// This is in correspondence with mPendingUpdates
|
||||
std::vector< Updater > mUpdaters;
|
||||
};
|
||||
|
||||
class AUDACITY_DLL_API TrackFactory
|
||||
|
|
|
@ -339,7 +339,8 @@ void TrackArtist::SetMargins(int left, int top, int right, int bottom)
|
|||
}
|
||||
|
||||
void TrackArtist::DrawTracks(TrackPanelDrawingContext &context,
|
||||
const std::function< Range() > &range,
|
||||
TrackList * tracks,
|
||||
Track * start,
|
||||
const wxRegion & reg,
|
||||
const wxRect & rect,
|
||||
const wxRect & clip,
|
||||
|
@ -351,10 +352,11 @@ void TrackArtist::DrawTracks(TrackPanelDrawingContext &context,
|
|||
{
|
||||
wxRect trackRect = rect;
|
||||
wxRect stereoTrackRect;
|
||||
TrackListIterator iter(tracks);
|
||||
Track *t;
|
||||
|
||||
bool hasSolo = false;
|
||||
for (auto &track : range()) {
|
||||
auto t = track.get();
|
||||
for (t = iter.First(); t; t = iter.Next()) {
|
||||
auto pt = dynamic_cast<const PlayableTrack *>(t);
|
||||
if (pt && pt->GetSolo()) {
|
||||
hasSolo = true;
|
||||
|
@ -377,8 +379,8 @@ void TrackArtist::DrawTracks(TrackPanelDrawingContext &context,
|
|||
|
||||
gPrefs->Read(wxT("/GUI/ShowTrackNameInWaveform"), &mbShowTrackNameInWaveform, false);
|
||||
|
||||
for (auto &track : range()) {
|
||||
auto t = track.get();
|
||||
t = iter.StartWith(start);
|
||||
while (t) {
|
||||
trackRect.y = t->GetY() - zoomInfo.vpos;
|
||||
trackRect.height = t->GetHeight();
|
||||
|
||||
|
@ -428,6 +430,8 @@ void TrackArtist::DrawTracks(TrackPanelDrawingContext &context,
|
|||
selectedRegion, zoomInfo,
|
||||
drawEnvelope, bigPoints, drawSliders, hasSolo);
|
||||
}
|
||||
|
||||
t = iter.Next();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,8 +24,6 @@
|
|||
#include "Experimental.h"
|
||||
#include "audacity/Types.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
class wxDC;
|
||||
class wxRect;
|
||||
class wxHashTable;
|
||||
|
@ -38,10 +36,10 @@ class WaveClip;
|
|||
class NoteTrack;
|
||||
class LabelTrack;
|
||||
class TimeTrack;
|
||||
class TrackList;
|
||||
class Ruler;
|
||||
class SelectedRegion;
|
||||
class ZoomInfo;
|
||||
class PendingTrackIterator;
|
||||
|
||||
struct TrackPanelDrawingContext;
|
||||
|
||||
|
@ -56,10 +54,8 @@ class AUDACITY_DLL_API TrackArtist {
|
|||
~TrackArtist();
|
||||
|
||||
void SetColours(int iColorIndex);
|
||||
|
||||
using Range = IteratorRange< PendingTrackIterator >;
|
||||
void DrawTracks(TrackPanelDrawingContext &context,
|
||||
const std::function< Range() > &range,
|
||||
TrackList *tracks, Track *start,
|
||||
const wxRegion & reg,
|
||||
const wxRect & rect, const wxRect & clip,
|
||||
const SelectedRegion &selectedRegion, const ZoomInfo &zoomInfo,
|
||||
|
|
|
@ -655,10 +655,6 @@ namespace
|
|||
// TODO: make a finer distinction between refreshing the track control area,
|
||||
// and the waveform area. As it is, redraw both whenever you must redraw either.
|
||||
|
||||
// Copy data from the underlying tracks to the pending tracks that are
|
||||
// really displayed
|
||||
panel->GetProject()->GetTracks()->UpdatePendingTracks();
|
||||
|
||||
using namespace RefreshCode;
|
||||
|
||||
if (refreshResult & DestroyedCell) {
|
||||
|
@ -1846,17 +1842,8 @@ void TrackPanel::DrawTracks(wxDC * dc)
|
|||
TrackPanelDrawingContext context{ *dc, Target(), mLastMouseState };
|
||||
|
||||
// The track artist actually draws the stuff inside each track
|
||||
auto project = GetProject();
|
||||
// Make a function that reproduces the range, because, quirkily, the range
|
||||
// can't be copied and iterated twice. Fix that later.
|
||||
auto range = [&]{
|
||||
return make_iterator_range(
|
||||
PendingTrackIterator{ project->GetTracks(),
|
||||
std::make_shared<VisibleTrackIterator>( project ) },
|
||||
PendingTrackIterator{}
|
||||
);
|
||||
};
|
||||
mTrackArtist->DrawTracks(context, range,
|
||||
auto first = GetProject()->GetFirstVisible();
|
||||
mTrackArtist->DrawTracks(context, GetTracks(), first.get(),
|
||||
region, tracksRect, clip,
|
||||
mViewInfo->selectedRegion, *mViewInfo,
|
||||
envelopeFlag, bigPointsFlag, sliderFlag);
|
||||
|
@ -1878,13 +1865,8 @@ void TrackPanel::DrawEverythingElse(TrackPanelDrawingContext &context,
|
|||
wxRect trackRect = clip;
|
||||
trackRect.height = 0; // for drawing background in no tracks case.
|
||||
|
||||
auto project = GetProject();
|
||||
PendingTrackIterator
|
||||
begin{ project->GetTracks(),
|
||||
std::make_shared<VisibleTrackIterator>( project ) },
|
||||
end;
|
||||
for (const auto &track : make_iterator_range(begin, end)) {
|
||||
auto t = track.get();
|
||||
VisibleTrackIterator iter(GetProject());
|
||||
for (Track *t = iter.First(); t; t = iter.Next()) {
|
||||
trackRect.y = t->GetY() - mViewInfo->vpos;
|
||||
trackRect.height = t->GetHeight();
|
||||
|
||||
|
@ -2879,15 +2861,15 @@ void TrackPanel::SetFocusedTrack( Track *t )
|
|||
if (t && !t->GetLinked() && t->GetLink())
|
||||
t = (WaveTrack*)t->GetLink();
|
||||
|
||||
if ( !mAx->SetFocus( Track::Pointer( t ) ) )
|
||||
return;
|
||||
|
||||
if (t && AudacityProject::GetKeyboardCaptureHandler())
|
||||
if (t && AudacityProject::GetKeyboardCaptureHandler()) {
|
||||
AudacityProject::ReleaseKeyboard(this);
|
||||
}
|
||||
|
||||
if (t)
|
||||
if (t) {
|
||||
AudacityProject::CaptureKeyboard(this);
|
||||
}
|
||||
|
||||
mAx->SetFocus( Track::Pointer( t ) );
|
||||
Refresh( false );
|
||||
}
|
||||
|
||||
|
|
|
@ -131,14 +131,8 @@ bool TrackPanelAx::IsFocused( Track *track )
|
|||
if( !focusedTrack )
|
||||
focusedTrack = SetFocus();
|
||||
|
||||
// Remap track pointer if there are oustanding pending updates
|
||||
auto origTrack =
|
||||
mTrackPanel->GetTracks()->FindById( track->GetId() );
|
||||
if (origTrack)
|
||||
track = origTrack;
|
||||
|
||||
if( ( track == focusedTrack.get() ) ||
|
||||
( focusedTrack && track == focusedTrack->GetLink() ) )
|
||||
( track == focusedTrack->GetLink() ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -148,8 +142,6 @@ bool TrackPanelAx::IsFocused( Track *track )
|
|||
|
||||
int TrackPanelAx::TrackNum( const std::shared_ptr<Track> &target )
|
||||
{
|
||||
// Find 1-based position of the target in the visible tracks, or 0 if not
|
||||
// found
|
||||
TrackListIterator iter( mTrackPanel->GetTracks() );
|
||||
Track *t = iter.First();
|
||||
int ndx = 0;
|
||||
|
|
|
@ -236,11 +236,11 @@ void UndoManager::ModifyState(const TrackList * l,
|
|||
|
||||
// Duplicate
|
||||
auto tracksCopy = TrackList::Create();
|
||||
for (auto t : *l) {
|
||||
if ( t->GetId() == TrackId{} )
|
||||
// Don't push a pending added track
|
||||
continue;
|
||||
TrackListConstIterator iter(l);
|
||||
const Track *t = iter.First();
|
||||
while (t) {
|
||||
tracksCopy->Add(t->Duplicate());
|
||||
t = iter.Next();
|
||||
}
|
||||
|
||||
// Replace
|
||||
|
@ -280,11 +280,11 @@ void UndoManager::PushState(const TrackList * l,
|
|||
}
|
||||
|
||||
auto tracksCopy = TrackList::Create();
|
||||
for (auto t : *l) {
|
||||
if ( t->GetId() == TrackId{} )
|
||||
// Don't push a pending added track
|
||||
continue;
|
||||
TrackListConstIterator iter(l);
|
||||
const Track *t = iter.First();
|
||||
while (t) {
|
||||
tracksCopy->Add(t->Duplicate());
|
||||
t = iter.Next();
|
||||
}
|
||||
|
||||
// Assume tags was duplicted before any changes.
|
||||
|
|
|
@ -452,7 +452,7 @@ float WaveTrack::GetChannelGain(int channel) const
|
|||
return right*mGain;
|
||||
}
|
||||
|
||||
void WaveTrack::DoSetMinimized(bool isMinimized){
|
||||
void WaveTrack::SetMinimized(bool isMinimized){
|
||||
|
||||
#ifdef EXPERIMENTAL_HALF_WAVE
|
||||
// Show half wave on collapse, full on restore.
|
||||
|
@ -465,7 +465,7 @@ void WaveTrack::DoSetMinimized(bool isMinimized){
|
|||
pWtvc->DoZoomPreset( isMinimized ? 1:0);
|
||||
#endif
|
||||
|
||||
PlayableTrack::DoSetMinimized( isMinimized );
|
||||
Track::SetMinimized( isMinimized );
|
||||
}
|
||||
|
||||
void WaveTrack::SetWaveColorIndex(int colorIndex)
|
||||
|
|
|
@ -132,7 +132,7 @@ class AUDACITY_DLL_API WaveTrack final : public PlayableTrack {
|
|||
// Takes gain and pan into account
|
||||
float GetChannelGain(int channel) const;
|
||||
|
||||
void DoSetMinimized(bool isMinimized) override;
|
||||
void SetMinimized(bool isMinimized) override;
|
||||
|
||||
int GetWaveColorIndex() const { return mWaveColorIndex; };
|
||||
void SetWaveColorIndex(int colorIndex);
|
||||
|
|
Loading…
Reference in New Issue
Block a user