Audacity 3.2.0
Track.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 Track.cpp
6
7 Dominic Mazzoni
8
9*******************************************************************//*******************************************************************/
17#include "Track.h"
18
19#include <algorithm>
20#include <cassert>
21#include <numeric>
22
23#include <float.h>
24#include <wx/file.h>
25#include <wx/textfile.h>
26#include <wx/log.h>
27
28#include "BasicUI.h"
29#include "Project.h"
30#include "UndoManager.h"
31
33
34#ifdef _MSC_VER
35//Disable truncation warnings
36#pragma warning( disable : 4786 )
37#endif
38
40{
41 mIndex = 0;
42}
43
45{
46 mIndex = 0;
47}
48
49// Copy all the track properties except the actual contents
50void Track::Init(const Track &orig)
51{
53 mId = orig.mId;
54}
55
56const wxString &Track::GetName() const
57{
58 return GetGroupData().mName;
59}
60
61void Track::SetName( const wxString &n )
62{
63 auto &name = GetGroupData().mName;
64 if (name != n) {
65 name = n;
66 Notify(true);
67 }
68}
69
71{
72 return GetGroupData().mSelected;
73}
74
76{
77 auto &selected = GetGroupData().mSelected;
78 if (selected != s) {
79 selected = s;
80 auto pList = mList.lock();
81 if (pList)
82 pList->SelectionEvent(*this);
83 }
84}
85
87{
88 assert(IsLeader());
89 // invoke "virtual constructor" to copy track object proper:
90 auto result = Clone(options.backup);
91
92 auto iter = TrackList::Channels(*result->begin()).begin();
93 const auto copyOne = [&](const Track *pChannel){
94 pChannel->AttachedTrackObjects::ForEach([&](auto &attachment){
95 // Copy view state that might be important to undo/redo
96 attachment.CopyTo(**iter);
97 });
98 ++iter;
99 };
100
101 if (GetOwner())
102 for (const auto pChannel : TrackList::Channels(this))
103 copyOne(pChannel);
104 else
105 copyOne(this);
106
107 return result;
108}
109
111{
112}
113
114
116{
117 wxASSERT(mList.lock() == NULL || this == mNode.first->get());
118 return mNode;
119}
120
122(const std::weak_ptr<TrackList> &list, TrackNodePointer node)
123{
124 // BUG: When using this function to clear an owner, we may need to clear
125 // focused track too. Otherwise focus could remain on an invisible (or deleted) track.
126 mList = list;
127 mNode = node;
128}
129
131{
132 return mIndex;
133}
134
135void Track::SetIndex(int index)
136{
137 mIndex = index;
138}
139
140void Track::SetLinkType(LinkType linkType, bool completeList)
141{
142 auto pList = mList.lock();
143 if (pList && pList->mPendingUpdates && !pList->mPendingUpdates->empty()) {
144 auto orig = pList->FindById( GetId() );
145 if (orig && orig != this) {
146 orig->SetLinkType(linkType);
147 return;
148 }
149 }
150
151 DoSetLinkType(linkType, completeList);
152
153 if (pList) {
154 pList->RecalcPositions(mNode);
155 pList->ResizingEvent(mNode);
156 }
157}
158
160{
161 auto pTrack = this;
162 if (auto pList = GetHolder())
163 if (auto pLeader = *pList->Find(pTrack))
164 pTrack = pLeader;
165 // May make on demand
166 return pTrack->ChannelGroup::GetGroupData();
167}
168
170{
171 return const_cast<Track *>(this)->GetGroupData();
172}
173
174void Track::DoSetLinkType(LinkType linkType, bool completeList)
175{
176 auto oldType = GetLinkType();
177 if (linkType == oldType)
178 // No change
179 return;
180
181 if (oldType == LinkType::None) {
182 // Becoming linked
183
184 // First ensure there is no partner
185 if (auto partner = GetLinkedTrack())
186 partner->DestroyGroupData();
187 assert(!GetLinkedTrack());
188
189 // Change the link type
190 GetGroupData().mLinkType = linkType;
191
192 // If this acquired a partner, it loses any old group data
193 if (auto partner = GetLinkedTrack())
194 partner->DestroyGroupData();
195 }
196 else if (linkType == LinkType::None) {
197 // Becoming unlinked
198 assert(FindGroupData());
199 if (HasLinkedTrack()) {
200 if (auto partner = GetLinkedTrack()) {
201 // Make independent copy of group data in the partner, which should
202 // have had none
203 assert(!partner->FindGroupData());
204 partner->ChannelGroup::Init(*this);
205 partner->GetGroupData().mLinkType = LinkType::None;
206 }
207 }
209 }
210 else {
211 // Remaining linked, changing the type
212 assert(FindGroupData());
213 GetGroupData().mLinkType = linkType;
214 }
215
216 // Assertion checks only in a debug build, does not have side effects!
217 assert(!completeList || LinkConsistencyCheck());
218}
219
221{
222 auto pList = static_cast<TrackList*>(mNode.second);
223 if (!pList)
224 return nullptr;
225
226 if (!pList->isNull(mNode)) {
227 if (HasLinkedTrack()) {
228 auto next = pList->getNext( mNode );
229 if ( !pList->isNull( next ) )
230 return next.first->get();
231 }
232
233 if (mNode.first != mNode.second->begin()) {
234 auto prev = pList->getPrev( mNode );
235 if ( !pList->isNull( prev ) ) {
236 auto track = prev.first->get();
237 if (track && track->HasLinkedTrack())
238 return track;
239 }
240 }
241 }
242
243 return nullptr;
244}
245
246bool Track::HasLinkedTrack() const noexcept
247{
248 auto pGroupData = FindGroupData();
249 return pGroupData && pGroupData->mLinkType != LinkType::None;
250}
251
252std::optional<TranslatableString> Track::GetErrorOpening() const
253{
254 return {};
255}
256
257void Track::Notify(bool allChannels, int code)
258{
259 auto pList = mList.lock();
260 if (pList)
261 pList->DataEvent(SharedPointer(), allChannels, code);
262}
263
264void Track::Paste(double t, const TrackList &src)
265{
266 Paste(t, **src.begin());
267}
268
269void Track::SyncLockAdjust(double oldT1, double newT1)
270{
271 assert(IsLeader());
272 const auto endTime = GetEndTime();
273 if (newT1 > oldT1 && oldT1 > endTime)
274 return;
275 if (newT1 > oldT1) {
276 auto cutChannels = Cut(oldT1, endTime);
277 assert(NChannels() == cutChannels->NChannels());
278 Paste(newT1, *cutChannels);
279 }
280 else if (newT1 < oldT1)
281 // Remove from the track
282 Clear(newT1, oldT1);
283}
284
285bool Track::Any() const
286 { return true; }
287
289 { return GetSelected(); }
290
291bool Track::IsLeader() const
292{
293 return !GetLinkedTrack() || HasLinkedTrack();
294}
295
297 { return IsSelected() && IsLeader(); }
298
300{
301 assert(!doFix || IsLeader());
302 // Sanity checks for linked tracks; unsetting the linked property
303 // doesn't fix the problem, but it likely leaves us with orphaned
304 // sample blocks instead of much worse problems.
305 bool err = false;
306 if (HasLinkedTrack()) /* which implies IsLeader() */ {
307 if (auto link = GetLinkedTrack()) {
308 // A linked track's partner should never itself be linked
309 if (link->HasLinkedTrack()) {
310 err = true;
311 if (doFix) {
312 wxLogWarning(
313 L"Left track %s had linked right track %s with extra right "
314 "track link.\n Removing extra link from right track.",
315 GetName(), link->GetName());
316 link->SetLinkType(LinkType::None);
317 }
318 }
319 }
320 else {
321 err = true;
322 if (doFix) {
323 wxLogWarning(
324 L"Track %s had link to NULL track. Setting it to not be linked.",
325 GetName());
327 }
328 }
329 }
330 return ! err;
331}
332
333// TrackList
334//
335// The TrackList sends events whenever certain updates occur to the list it
336// is managing. Any other classes that may be interested in get these updates
337// should use TrackList::Subscribe().
338//
339
340// same value as in the default constructed TrackId:
341long TrackList::sCounter = -1;
342
345};
346
348{
349 return project.AttachedObjects::Get< TrackList >( key );
350}
351
353{
354 return Get( const_cast< AudacityProject & >( project ) );
355}
356
358 : mOwner{ pOwner }
359{
360 if (mOwner)
361 mPendingUpdates = Temporary(nullptr);
362}
363
364// Factory function
366{
367 return std::make_shared<TrackList>(pOwner);
368}
369
370#if 0
372{
373 if (this != &that) {
374 this->Clear();
375 Swap(that);
376 }
377 return *this;
378}
379#endif
382{
383 auto SwapLOTs = [](
384 ListOfTracks &a, const std::weak_ptr< TrackList > &aSelf,
385 ListOfTracks &b, const std::weak_ptr< TrackList > &bSelf )
386 {
387 a.swap(b);
388 for (auto it = a.begin(), last = a.end(); it != last; ++it)
389 (*it)->SetOwner(aSelf, {it, &a});
390 for (auto it = b.begin(), last = b.end(); it != last; ++it)
391 (*it)->SetOwner(bSelf, {it, &b});
392 };
393
394 const auto self = shared_from_this();
395 const auto otherSelf = that.shared_from_this();
396 SwapLOTs( *this, self, that, otherSelf );
397
398 assert(!GetOwner() && !that.GetOwner()); // precondition
399 // which implies (see constructor)
400 assert(!this->mPendingUpdates);
401 assert(!that.mPendingUpdates);
402
403 mUpdaters.swap(that.mUpdaters);
404}
405
407{
408 Clear(false);
409}
410
411wxString TrackList::MakeUniqueTrackName(const wxString& baseTrackName) const
412{
413 int n = 1;
414 while(true) {
415 auto name = wxString::Format("%s %d", baseTrackName, n++);
416
417 bool found {false};
418 for(const auto track : Tracks<const Track>()) {
419 if(track->GetName() == name) {
420 found = true;
421 break;
422 }
423 }
424 if(!found)
425 return name;
426 }
427}
428
430{
431 if (isNull(node))
432 return;
433
434 Track *t;
435 int i = 0;
436
437 auto prev = getPrev(node);
438 if (!isNull(prev)) {
439 t = prev.first->get();
440 i = t->GetIndex() + 1;
441 }
442
443 const auto theEnd = End();
444 for (auto n = DoFind(node.first->get()); n != theEnd; ++n) {
445 t = *n;
446 t->SetIndex(i++);
447 }
448
450}
451
453{
454 BasicUI::CallAfter( [wThis = weak_from_this(), event = std::move(event)]{
455 if (auto pThis = wThis.lock())
456 pThis->Publish(event);
457 } );
458}
459
461{
462 for (auto channel : Channels(&track))
463 QueueEvent({
464 TrackListEvent::SELECTION_CHANGE, channel->shared_from_this() });
465}
466
468 const std::shared_ptr<Track> &pTrack, bool allChannels, int code)
469{
470 auto doQueueEvent = [this, code](const std::shared_ptr<Track> &theTrack){
472 };
473 if (allChannels)
474 for (auto channel : Channels(pTrack.get()))
475 doQueueEvent(channel->shared_from_this());
476 else
477 doQueueEvent(pTrack);
479
481{
482 QueueEvent({ TrackListEvent::PERMUTED, *node.first });
483}
484
485void TrackList::DeletionEvent(std::weak_ptr<Track> node, bool duringReplace)
486{
488 { TrackListEvent::DELETION, std::move(node), duringReplace ? 1 : 0 });
489}
490
492{
493 QueueEvent({ TrackListEvent::ADDITION, *node.first });
494}
495
497{
498 QueueEvent({ TrackListEvent::RESIZING, *node.first });
499}
500
503{
504 auto it = const_cast<TrackList*>(this)->getEnd();
505 return {
506 { it, it, it, &Track::Any },
507 { it, it, it, &Track::Any }
508 };
509}
510
512{
513 if (!pTrack || pTrack->GetHolder() != this)
514 return EndIterator<Track>();
515 else
516 return MakeTrackIterator<Track>(pTrack->GetNode());
517}
518
520{
521 auto iter = DoFind(pTrack);
522 while( *iter && ! ( *iter )->IsLeader() )
523 --iter;
524 return iter.Filter( &Track::IsLeader );
525}
526
528{
529 if (!track.HasLinkedTrack())
530 return nullptr;
531 auto pOwner = track.GetOwner();
532 if (!pOwner)
533 return nullptr;
534 auto pPartner = pOwner->GetNext(&track, false);
535 if (!pPartner)
536 return nullptr;
537
538 // Swap channels, avoiding copying of GroupData
539 auto pData = track.DetachGroupData();
540 assert(pData);
541 pOwner->MoveUp(pPartner);
542 pPartner->AssignGroupData(move(pData));
543 return pPartner;
544}
545
546void TrackList::Insert(const Track* before, TrackList&& trackList)
547{
548 assert(before == nullptr || (before->IsLeader() && Find(before) != EndIterator<const Track>()));
549
550 if(before == nullptr)
551 {
552 Append(std::move(trackList));
553 return;
554 }
555
556 std::vector<Track *> arr;
557 arr.reserve(Size() + trackList.Size());
558 for (const auto track : *this) {
559 if (track == before)
560 {
561 for(const auto addedTrack : trackList)
562 arr.push_back(addedTrack);
563 }
564 arr.push_back(track);
565 }
566 Append(std::move(trackList));
567 Permute(arr);
568}
569
570void TrackList::Permute(const std::vector<Track *> &tracks)
571{
572 std::vector<TrackNodePointer> permutation;
573 for (const auto pTrack : tracks)
574 for (const auto pChannel : Channels(pTrack))
575 permutation.push_back(pChannel->GetNode());
576 for (const auto iter : permutation) {
577 ListOfTracks::value_type track = *iter.first;
578 erase(iter.first);
579 Track *pTrack = track.get();
580 pTrack->SetOwner(shared_from_this(),
581 { insert(ListOfTracks::end(), track), this });
582 }
583 auto n = getBegin();
586}
587
589{
590 // Linear search. Tracks in a project are usually very few.
591 // Search only the non-pending tracks.
592 auto it = std::find_if( ListOfTracks::begin(), ListOfTracks::end(),
593 [=](const ListOfTracks::value_type &ptr){ return ptr->GetId() == id; } );
594 if (it == ListOfTracks::end())
595 return {};
596 return it->get();
597}
598
599Track *TrackList::DoAddToHead(const std::shared_ptr<Track> &t)
600{
601 Track *pTrack = t.get();
602 push_front(ListOfTracks::value_type(t));
603 auto n = getBegin();
604 pTrack->SetOwner(shared_from_this(), n);
605 pTrack->SetId( TrackId{ ++sCounter } );
607 AdditionEvent(n);
608 return front().get();
609}
610
611Track *TrackList::DoAdd(const std::shared_ptr<Track> &t)
612{
613 if (!ListOfTracks::empty()) {
614 auto &pLast = *ListOfTracks::rbegin();
615 if (auto pGroupData = pLast->FindGroupData()
616 ; pGroupData && pGroupData->mLinkType != Track::LinkType::None
617 ) {
618 // Assume the newly added track is intended to pair with the last
619 // Avoid upsetting assumptions in case this track had its own group
620 // data initialized during Duplicate()
621 t->DestroyGroupData();
622 }
623 }
624
625 push_back(t);
626
627 auto n = getPrev( getEnd() );
628
629 t->SetOwner(shared_from_this(), n);
630 if (mAssignsIds)
631 t->SetId(TrackId{ ++sCounter });
633 AdditionEvent(n);
634 return back().get();
635}
636
638{
639 assert(t.IsLeader());
640 assert(t.GetOwner().get() == this);
641 auto nChannels = t.NChannels();
642
643 // TODO wide wave tracks: This won't matter, tracks will be 1 to 1
644 assert(nChannels >= (*with.begin())->NChannels());
645
646 TrackListHolder result = Temporary(nullptr);
647
648 auto iter = with.ListOfTracks::begin(),
649 end = with.ListOfTracks::end();
650 bool isLeader = true;
651 std::vector<Track*> saveChannels;
652 for (const auto pChannel : TrackList::Channels(&t))
653 saveChannels.push_back(pChannel);
654
655 // Because default constructor doesn't work
656 std::optional<TrackNodePointer> lastNode;
657
658 for (const auto pChannel : saveChannels) {
659 auto spChannel = pChannel->shared_from_this();
660
662 auto node = pChannel->GetNode();
663 pChannel->SetOwner({}, {});
664 result->Add(spChannel);
665
667 assert(isLeader == pChannel->IsLeader());
668 isLeader = false;
669
670 if (iter == end) {
671 node.second->erase(node.first);
672 RecalcPositions(*lastNode);
673 DeletionEvent(spChannel, true);
674 }
675 else {
676 lastNode.emplace(node);
678 const auto pTrack = *iter;
679 *node.first = pTrack;
680 iter = with.erase(iter);
681 pTrack->SetOwner(shared_from_this(), node);
682 pTrack->SetId(pChannel->GetId());
683 RecalcPositions(node);
684 DeletionEvent(spChannel, true);
685 AdditionEvent(node);
686 }
687 }
688 return result;
689}
690
691std::vector<Track*> TrackList::UnlinkChannels(Track& track)
692{
693 auto list = track.mList.lock();
694 if (list.get() == this)
695 {
696 auto channels = TrackList::Channels(&track);
697 for (auto c : channels)
698 c->SetLinkType(Track::LinkType::None);
699 return { channels.begin(), channels.end() };
700 }
701 else
703}
704
705bool TrackList::MakeMultiChannelTrack(Track& track, int nChannels)
706{
707 if (nChannels != 2)
708 return false;
709
710 auto list = track.mList.lock();
711 if (list.get() == this) {
712 if (*list->Find(&track) != &track)
713 return false;
714
715 auto first = list->DoFind(&track);
716 auto canLink = [&]() -> bool {
717 int count = nChannels;
718 for (auto it = first, end = TrackList::End(); it != end && count; ++it)
719 {
720 if ((*it)->HasLinkedTrack())
721 return false;
722 --count;
723 }
724 return count == 0;
725 }();
726
727 if (!canLink)
728 return false;
729
730 (*first)->SetLinkType(Track::LinkType::Aligned);
731
732 //Cleanup the group data in all channels except the first
733 for(auto it = std::next(first), last = std::next(first, nChannels);
734 it != last;
735 ++it)
736 {
737 (*it)->DestroyGroupData();
738 }
739 }
740 else
742 return true;
743}
744
746{
747 assert(track.IsLeader());
748 auto *t = &track;
749 const size_t nChannels = NChannels(*t);
750 Track *nextT{};
751 for (size_t ii = 0; t != nullptr && ii < nChannels; ++ii, t = nextT) {
752 nextT = nullptr;
753 auto iter = getEnd();
754 auto node = t->GetNode();
755 t->SetOwner({}, {});
756
757 if (!isNull(node)) {
758 ListOfTracks::value_type holder = *node.first;
759
760 iter = getNext(node);
761 erase(node.first);
762 if (!isNull(iter)) {
763 RecalcPositions(iter);
764 nextT = iter.first->get();
765 }
766
767 DeletionEvent(t->shared_from_this(), false);
768 }
769 }
770}
771
772void TrackList::Clear(bool sendEvent)
773{
774 // Null out the back-pointers to this in tracks, in case there
775 // are outstanding shared_ptrs to those tracks, making them outlive
776 // the temporary ListOfTracks below.
777 for (auto pTrack: Tracks<Track>()) {
778 pTrack->SetOwner({}, {});
779
780 if (sendEvent)
781 DeletionEvent(pTrack->shared_from_this(), false);
782 }
783
784 if (mPendingUpdates)
785 for (auto pTrack: static_cast<ListOfTracks&>(*mPendingUpdates)) {
786 pTrack->SetOwner({}, {});
787 if (sendEvent)
788 DeletionEvent(pTrack, false);
789 }
790
791 ListOfTracks tempList;
792 tempList.swap( *this );
793
794 if (mPendingUpdates)
795 mPendingUpdates = Temporary(nullptr);
796
797 mUpdaters.clear();
798}
799
801Track *TrackList::GetNext(Track * t, bool linked) const
802{
803 if (t) {
804 auto node = t->GetNode();
805 if ( !isNull( node ) ) {
806 if ( linked && t->HasLinkedTrack() )
807 node = getNext( node );
808
809 if ( !isNull( node ) )
810 node = getNext( node );
811
812 if ( !isNull( node ) )
813 return node.first->get();
814 }
815 }
816
817 return nullptr;
818}
819
820Track *TrackList::GetPrev(Track * t, bool linked) const
821{
822 if (t) {
823 TrackNodePointer prev;
824 auto node = t->GetNode();
825 if ( !isNull( node ) ) {
826 // linked is true and input track second in team?
827 if (linked) {
828 prev = getPrev( node );
829 if( !isNull( prev ) &&
830 !t->HasLinkedTrack() && t->GetLinkedTrack() )
831 // Make it the first
832 node = prev;
833 }
834
835 prev = getPrev( node );
836 if ( !isNull( prev ) ) {
837 // Back up once
838 node = prev;
839
840 // Back up twice sometimes when linked is true
841 if (linked) {
842 prev = getPrev( node );
843 if( !isNull( prev ) &&
844 !(*node.first)->HasLinkedTrack() && (*node.first)->GetLinkedTrack() )
845 node = prev;
846 }
847
848 return node.first->get();
849 }
850 }
851 }
852
853 return nullptr;
854}
855
857{
858 return GetPrev(t, true) != NULL;
859}
860
862{
863 return GetNext(t, true) != NULL;
864}
865
866// This is used when you want to swap the channel group starting
867// at s1 with that starting at s2.
868// The complication is that the tracks are stored in a single
869// linked list.
871{
872 // if a null pointer is passed in, we want to know about it
873 wxASSERT(!isNull(s1));
874 wxASSERT(!isNull(s2));
875
876 // Deal with first track in each team
877 s1 = ( * Find( s1.first->get() ) )->GetNode();
878 s2 = ( * Find( s2.first->get() ) )->GetNode();
879
880 // Safety check...
881 if (s1 == s2)
882 return;
883
884 // Be sure s1 is the earlier iterator
885 if ((*s1.first)->GetIndex() >= (*s2.first)->GetIndex())
886 std::swap(s1, s2);
887
888 // For saving the removed tracks
889 using Saved = std::vector< ListOfTracks::value_type >;
890 Saved saved1, saved2;
891
892 auto doSave = [&] ( Saved &saved, TrackNodePointer &s ) {
893 size_t nn = NChannels(**s.first);
894 saved.resize( nn );
895 // Save them in backwards order
896 while( nn-- )
897 saved[nn] = *s.first, s.first = erase(s.first);
898 };
899
900 doSave( saved1, s1 );
901 // The two ranges are assumed to be disjoint but might abut
902 const bool same = (s1 == s2);
903 doSave( saved2, s2 );
904 if (same)
905 // Careful, we invalidated s1 in the second doSave!
906 s1 = s2;
907
908 // Reinsert them
909 auto doInsert = [&] ( Saved &saved, TrackNodePointer &s ) {
910 Track *pTrack;
911 for (auto & pointer : saved)
912 pTrack = pointer.get(),
913 // Insert before s, and reassign s to point at the new node before
914 // old s; which is why we saved pointers in backwards order
915 pTrack->SetOwner(shared_from_this(),
916 s = { insert(s.first, pointer), this } );
917 };
918 // This does not invalidate s2 even when it equals s1:
919 doInsert( saved2, s1 );
920 // Even if s2 was same as s1, this correctly inserts the saved1 range
921 // after the saved2 range, when done after:
922 doInsert( saved1, s2 );
923
924 // Now correct the Index in the tracks, and other things
925 RecalcPositions(s1);
927}
928
930{
931 if (t) {
932 Track *p = GetPrev(t, true);
933 if (p) {
934 SwapNodes(p->GetNode(), t->GetNode());
935 return true;
936 }
937 }
938
939 return false;
940}
941
943{
944 if (t) {
945 Track *n = GetNext(t, true);
946 if (n) {
947 SwapNodes(t->GetNode(), n->GetNode());
948 return true;
949 }
950 }
951
952 return false;
953}
954
956{
957 return Begin() == End();
958}
959
961{
962 int cnt = 0;
963
964 if (!empty())
965 cnt = getPrev( getEnd() ).first->get()->GetIndex() + 1;
966
967 return cnt;
968}
969
970namespace {
971 // Abstract the common pattern of the following two member functions
972 inline double Accumulate(const TrackList &list,
973 double (Track::*memfn)() const, double ident,
974 const double &(*combine)(const double&, const double&))
975 {
976 // Default the answer to zero for empty list
977 if (list.empty())
978 return 0.0;
979
980 // Otherwise accumulate minimum or maximum of track values
981 return list.Any().accumulate(ident, combine, memfn);
982 }
983}
984
986{
987 return Accumulate(*this, &Track::GetStartTime,
988 std::numeric_limits<double>::max(), std::min);
989}
990
992{
993 return Accumulate(*this, &Track::GetEndTime,
994 std::numeric_limits<double>::lowest(), std::max);
995}
996
997Track*
999{
1000 // This is only done on the TrackList belonging to a project
1001 assert(GetOwner()); // which implies mPendingUpdates is not null
1002 assert(src->IsLeader());
1003
1004 auto tracks = src->Clone(false); // not duplicate
1005 assert(src->NChannels() == tracks->NChannels());
1006 {
1007 // Share the satellites with the original, though they do not point back
1008 // to the pending track
1009 const auto channels = TrackList::Channels(src);
1010 auto iter = TrackList::Channels(*tracks->begin()).begin();
1011 for (const auto pChannel : channels)
1012 ((AttachedTrackObjects&)**iter++) = *pChannel; // shallow copy
1013 }
1014
1015 const auto result = *tracks->begin();
1016 mUpdaters.push_back(updater);
1017 auto iter = tracks->ListOfTracks::begin(),
1018 end = tracks->ListOfTracks::end();
1019 while (iter != end) {
1020 auto pTrack = *iter;
1021 iter = tracks->erase(iter);
1022 mPendingUpdates->ListOfTracks::push_back(pTrack->SharedPointer());
1023 auto n = mPendingUpdates->ListOfTracks::end();
1024 --n;
1025 pTrack->SetOwner(shared_from_this(), {n, &*mPendingUpdates});
1026 }
1027 return result;
1028}
1029
1031{
1032 if (!mPendingUpdates)
1033 return;
1034 auto pUpdater = mUpdaters.begin();
1035 for (const auto &pendingTrack : *mPendingUpdates) {
1036 auto src = FindById(pendingTrack->GetId());
1037 // Copy just a part of the track state, according to the update
1038 // function
1039 const auto &updater = *pUpdater;
1040 if (pendingTrack && src) {
1041 if (updater)
1042 updater(*pendingTrack, *src);
1043 }
1044 ++pUpdater;
1045 pendingTrack->DoSetLinkType(src->GetLinkType());
1046 }
1047}
1048
1051{
1052 assert(GetOwner()); // which implies mPendingUpdates is not null
1053 for (const auto &pTrack: static_cast<ListOfTracks&>(*mPendingUpdates))
1054 pTrack->SetOwner( {}, {} );
1055 mPendingUpdates->ListOfTracks::clear();
1056 mUpdaters.clear();
1057
1058 if (pAdded)
1059 pAdded->clear();
1060
1061 // To find the first node that remains after the first deleted one
1062 TrackNodePointer node;
1063 bool foundNode = false;
1064
1065 for (auto it = ListOfTracks::begin(), stop = ListOfTracks::end();
1066 it != stop;) {
1067 if (it->get()->GetId() == TrackId{}) {
1068 do {
1069 if (pAdded)
1070 pAdded->push_back( *it );
1071 (*it)->SetOwner( {}, {} );
1072 DeletionEvent(*it, false);
1073 it = erase( it );
1074 }
1075 while (it != stop && it->get()->GetId() == TrackId{});
1076
1077 if (!foundNode && it != stop) {
1078 node = (*it)->GetNode();
1079 foundNode = true;
1080 }
1081 }
1082 else
1083 ++it;
1084 }
1085
1086 if (!empty()) {
1088 }
1089}
1090
1093{
1094 bool result = false;
1095
1096 ListOfTracks additions;
1097 auto updates = Temporary(nullptr);
1098 {
1099 // Always clear, even if one of the update functions throws
1100 auto cleanup = finally( [&] { ClearPendingTracks( &additions ); } );
1102 updates.swap(mPendingUpdates);
1103 }
1104
1105 // Remaining steps must be No-fail-guarantee so that this function
1106 // gives Strong-guarantee
1107
1108 std::vector< std::shared_ptr<Track> > reinstated;
1109
1110 if (updates)
1111 for (auto pendingTrack : static_cast<ListOfTracks &>(*updates))
1112 pendingTrack->AttachedTrackObjects::ForEach([&](auto &attachment){
1113 attachment.Reparent( pendingTrack );
1114 });
1115 while (updates && !updates->empty()) {
1116 auto iter = updates->ListOfTracks::begin();
1117 auto pendingTrack = *iter;
1118 auto src = FindById(pendingTrack->GetId());
1119 if (src) {
1120 this->ReplaceOne(*src, std::move(*updates));
1121 result = true;
1122 }
1123 else {
1124 // Perhaps a track marked for pending changes got deleted by
1125 // some other action. Recreate it so we don't lose the
1126 // accumulated changes.
1127 reinstated.push_back(pendingTrack);
1128 updates->ListOfTracks::erase(iter);
1129 }
1130 }
1131
1132 // If there are tracks to reinstate, append them to the list.
1133 for (auto &pendingTrack : reinstated)
1134 if (pendingTrack)
1135 this->Add( pendingTrack ), result = true;
1136
1137 // Put the pending added tracks back into the list, preserving their
1138 // positions.
1139 bool inserted = false;
1140 ListOfTracks::iterator first;
1141 for (auto &pendingTrack : additions) {
1142 if (pendingTrack) {
1143 auto iter = ListOfTracks::begin();
1144 std::advance( iter, pendingTrack->GetIndex() );
1145 iter = ListOfTracks::insert( iter, pendingTrack );
1146 pendingTrack->SetOwner( shared_from_this(), {iter, this} );
1147 pendingTrack->SetId( TrackId{ ++sCounter } );
1148 if (!inserted) {
1149 first = iter;
1150 inserted = true;
1151 }
1152 }
1153 }
1154 if (inserted) {
1155 TrackNodePointer node{first, this};
1156 RecalcPositions(node);
1157 AdditionEvent(node);
1158 result = true;
1159 }
1160
1161 return result;
1162}
1163
1165{
1166 // Linear search. Tracks in a project are usually very few.
1167 auto pList = mList.lock();
1168 if (pList && pList->mPendingUpdates) {
1169 const auto id = GetId();
1170 const auto end = pList->mPendingUpdates->ListOfTracks::end();
1171 auto it = std::find_if(
1172 pList->mPendingUpdates->ListOfTracks::begin(), end,
1173 [=](const ListOfTracks::value_type &ptr){ return ptr->GetId() == id; } );
1174 if (it != end)
1175 return *it;
1176 }
1177 return SharedPointer();
1178}
1179
1180std::shared_ptr<const Track> Track::SubstitutePendingChangedTrack() const
1181{
1182 return const_cast<Track*>(this)->SubstitutePendingChangedTrack();
1183}
1184
1185std::shared_ptr<const Track> Track::SubstituteOriginalTrack() const
1186{
1187 auto pList = mList.lock();
1188 if (pList && pList->mPendingUpdates) {
1189 const auto id = GetId();
1190 const auto pred = [=]( const ListOfTracks::value_type &ptr ) {
1191 return ptr->GetId() == id; };
1192 const auto end = pList->mPendingUpdates->ListOfTracks::end();
1193 const auto it =
1194 std::find_if(pList->mPendingUpdates->ListOfTracks::begin(), end, pred);
1195 if (it != end) {
1196 const auto &list2 = (const ListOfTracks &) *pList;
1197 const auto end2 = list2.end();
1198 const auto it2 = std::find_if( list2.begin(), end2, pred );
1199 if ( it2 != end2 )
1200 return *it2;
1201 }
1202 }
1203 return SharedPointer();
1204}
1205
1207{
1208 static Track::TypeInfo info{
1209 { "generic", "generic", XO("Generic Track") }, false };
1210 return info;
1211}
1212
1214{
1215 return true;
1216}
1217
1218// Serialize, not with tags of its own, but as attributes within a tag.
1220 XMLWriter &xmlFile, bool includeNameAndSelected) const
1221{
1222 if (includeNameAndSelected) {
1223 // May write name and selectedness redundantly for right channels,
1224 // but continue doing that in case the file is opened in Audacity 3.1.x
1225 // which does not have unique ChannelGroupData for the track
1226 xmlFile.WriteAttr(wxT("name"), GetName());
1227 xmlFile.WriteAttr(wxT("isSelected"), this->GetSelected());
1228 }
1229 AttachedTrackObjects::ForEach([&](auto &attachment){
1230 attachment.WriteXMLAttributes( xmlFile );
1231 });
1232}
1233
1234// Return true iff the attribute is recognized.
1236 const std::string_view& attr, const XMLAttributeValueView& valueView)
1237{
1238 long nValue = -1;
1239
1240 bool handled = false;
1241 AttachedTrackObjects::ForEach([&](auto &attachment){
1242 handled = handled || attachment.HandleXMLAttribute( attr, valueView );
1243 });
1244 if (handled)
1245 ;
1246 // Note that the per-group properties of name and selectedness may have
1247 // been written redundantly for each channel, and values for the last
1248 // channel will be the last ones assigned
1249 else if (attr == "name") {
1250 SetName(valueView.ToWString());
1251 return true;
1252 }
1253 else if (attr == "isSelected" && valueView.TryGet(nValue)) {
1254 this->SetSelected(nValue != 0);
1255 return true;
1256 }
1257 return false;
1258}
1259
1261{
1262 auto pList = mList.lock();
1263 if (pList) {
1264 pList->RecalcPositions(mNode);
1265 pList->ResizingEvent(mNode);
1266 }
1267}
1268
1270{
1271 if (mPendingUpdates && !mPendingUpdates->empty())
1272 return true;
1273 if (End() != std::find_if(Begin(), End(), [](const Track *t){
1274 return t->GetId() == TrackId{};
1275 }))
1276 return true;
1277 return false;
1278}
1279
1281{
1282 const auto pGroupData = FindGroupData();
1283 return pGroupData ? pGroupData->mLinkType : LinkType::None;
1284}
1285
1288 Track &track, size_t iChannel)
1289{
1290 // Precondition of this function; satisfies precondition of factory below
1291 assert(iChannel < track.NChannels());
1292 auto &attachments = track.AttachedObjects::Get<ChannelAttachmentsBase>(key);
1293 auto &objects = attachments.mAttachments;
1294 if (iChannel >= objects.size())
1295 objects.resize(iChannel + 1);
1296 auto &pObject = objects[iChannel];
1297 if (!pObject) {
1298 // Create on demand
1299 pObject = attachments.mFactory(track, iChannel);
1300 assert(pObject); // Precondition of constructor
1301 }
1302 return *pObject;
1303}
1304
1307 Track *pTrack, size_t iChannel)
1308{
1309 assert(!pTrack || iChannel < pTrack->NChannels());
1310 if (!pTrack)
1311 return nullptr;
1312 const auto pAttachments =
1313 pTrack->AttachedObjects::Find<ChannelAttachmentsBase>(key);
1314 // do not create on demand
1315 if (!pAttachments || iChannel >= pAttachments->mAttachments.size())
1316 return nullptr;
1317 return pAttachments->mAttachments[iChannel].get();
1318}
1319
1321 : mFactory{ move(factory) }
1322{
1323 // Always construct one channel view
1324 // TODO wide wave tracks -- number of channels will be known earlier, and
1325 // they will all be constructed
1326 mAttachments.push_back(mFactory(track, 0));
1327}
1328
1330
1332{
1333 for (auto &pAttachment : mAttachments)
1334 if (pAttachment)
1335 pAttachment->CopyTo(track);
1336}
1337
1338void ChannelAttachmentsBase::Reparent(const std::shared_ptr<Track> &parent)
1339{
1340 for (auto &pAttachment : mAttachments)
1341 if (pAttachment)
1342 pAttachment->Reparent(parent);
1343}
1344
1346{
1347 for (auto &pAttachment : mAttachments)
1348 if (pAttachment)
1349 pAttachment->WriteXMLAttributes(writer);
1350}
1351
1353 const std::string_view& attr, const XMLAttributeValueView& valueView)
1354{
1355 return std::any_of(mAttachments.begin(), mAttachments.end(),
1356 [&](auto &pAttachment) {
1357 return pAttachment && pAttachment->HandleXMLAttribute(attr, valueView);
1358 });
1359}
1360
1361void Track::OnProjectTempoChange(double newTempo)
1362{
1363 assert(IsLeader());
1364 auto &mProjectTempo = GetGroupData().mProjectTempo;
1365 DoOnProjectTempoChange(mProjectTempo, newTempo);
1366 mProjectTempo = newTempo;
1367}
1368
1369const std::optional<double>& Track::GetProjectTempo() const
1370{
1371 return GetGroupData().mProjectTempo;
1372}
1373
1374// Undo/redo handling of selection changes
1375namespace {
1378 : mpTracks{ TrackList::Create(nullptr) }
1379 {
1380 for (auto pTrack : TrackList::Get(project)) {
1381 if (pTrack->GetId() == TrackId{})
1382 // Don't copy a pending added track
1383 continue;
1384 mpTracks->Append(std::move(*pTrack->Duplicate()));
1385 }
1386 }
1388 auto &dstTracks = TrackList::Get(project);
1389 dstTracks.Clear();
1390 for (auto pTrack : *mpTracks)
1391 dstTracks.Append(std::move(*pTrack->Duplicate()));
1392 }
1393 bool CanUndoOrRedo(const AudacityProject &project) override {
1395 }
1396 const std::shared_ptr<TrackList> mpTracks;
1397};
1398
1400 [](AudacityProject &project) -> std::shared_ptr<UndoStateExtension> {
1401 return std::make_shared<TrackListRestorer>(project);
1402 }
1403};
1404}
1405
1407{
1408 auto &exts = state.state.extensions;
1409 auto end = exts.end(),
1410 iter = std::find_if(exts.begin(), end, [](auto &pExt){
1411 return dynamic_cast<TrackListRestorer*>(pExt.get());
1412 });
1413 if (iter != end)
1414 return static_cast<TrackListRestorer*>(iter->get())->mpTracks.get();
1415 return nullptr;
1416}
1417
1419 const Track::Holder &left, const Track::Holder &right)
1420{
1421 assert(left == nullptr || left->GetOwner() == nullptr);
1422 assert(right == nullptr || (left && right->GetOwner() == nullptr));
1423 // Make a well formed channel group from these tracks
1424 auto tempList = Create(pProject);
1425 if (left) {
1426 tempList->Add(left);
1427 if (right) {
1428 tempList->Add(right);
1429 tempList->MakeMultiChannelTrack(*left, 2);
1430 }
1431 }
1432 tempList->mAssignsIds = false;
1433 return tempList;
1434}
1435
1437 const std::vector<Track::Holder> &channels)
1438{
1439 size_t iChannel = 0;
1440 auto nChannels = channels.size();
1441 auto left = (nChannels == 2 ? channels[iChannel++] : nullptr);
1442 auto right = (nChannels == 2 ? channels[iChannel++] : nullptr);
1443 auto tempList = Temporary(pProject, left, right);
1444 for (; iChannel < nChannels; ++iChannel)
1445 tempList->Add(channels[iChannel]);
1446 return tempList;
1447}
1448
1450{
1451 auto iter = list.ListOfTracks::begin(),
1452 end = list.ListOfTracks::end();
1453 while (iter != end) {
1454 auto pTrack = *iter;
1455 iter = list.erase(iter);
1456 this->Add(pTrack);
1457 }
1458}
1459
1461{
1462 for(auto it = list.ListOfTracks::begin(); it != list.ListOfTracks::end();)
1463 {
1464 Add(*it);
1465 (*it)->SetId({});
1466 it = list.erase(it);
1467 }
1468}
1469
1471{
1472 auto iter = list.ListOfTracks::begin(),
1473 end = list.ListOfTracks::end();
1474 if (iter != end) {
1475 for (size_t nn = TrackList::NChannels(**iter); nn--;) {
1476 auto pTrack = *iter;
1477 iter = list.erase(iter);
1478 this->Add(pTrack);
1479 }
1480 }
1481}
wxT("CloseDown"))
Toolkit-neutral facade for basic user interface services.
int min(int a, int b)
const TranslatableString name
Definition: Distortion.cpp:76
XO("Cut/Copy/Paste")
static const auto exts
Definition: ImportAUP.cpp:56
MessageBoxException for violation of preconditions or assertions.
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
static CommandHandlerObject & ident(AudacityProject &project)
const auto tracks
const auto project
static const AudacityProject::AttachedObjects::RegisteredFactory key
Definition: Track.cpp:343
declares abstract base class Track, TrackList, and iterators over TrackList
std::pair< ListOfTracks::iterator, ListOfTracks * > TrackNodePointer
Pairs a std::list iterator and a pointer to a list, for comparison purposes.
Definition: Track.h:51
std::list< std::shared_ptr< Track > > ListOfTracks
Definition: Track.h:45
std::shared_ptr< TrackList > TrackListHolder
Definition: Track.h:42
int id
static CustomUpdaterValue updater
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
Holds multiple objects as a single attachment to Track.
Definition: Track.h:530
const Factory mFactory
Definition: Track.h:561
static TrackAttachment & Get(const AttachedTrackObjects::RegisteredFactory &key, Track &track, size_t iChannel)
Definition: Track.cpp:1286
std::vector< std::shared_ptr< TrackAttachment > > mAttachments
Definition: Track.h:562
~ChannelAttachmentsBase() override
void WriteXMLAttributes(XMLWriter &writer) const override
Serialize persistent attributes.
Definition: Track.cpp:1345
void Reparent(const std::shared_ptr< Track > &parent) override
Object may be shared among tracks but hold a special back-pointer to one of them; reassign it.
Definition: Track.cpp:1338
static TrackAttachment * Find(const AttachedTrackObjects::RegisteredFactory &key, Track *pTrack, size_t iChannel)
Definition: Track.cpp:1305
bool HandleXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &valueView) override
Deserialize an attribute, returning true if recognized.
Definition: Track.cpp:1352
void CopyTo(Track &track) const override
Copy state, for undo/redo purposes.
Definition: Track.cpp:1331
std::function< std::shared_ptr< TrackAttachment >(Track &, size_t)> Factory
Definition: Track.h:533
ChannelAttachmentsBase(Track &track, Factory factory)
Definition: Track.cpp:1320
double GetEndTime() const
Get the maximum of End() values of intervals, or 0 when none.
Definition: Channel.cpp:135
double GetStartTime() const
Get the minimum of Start() values of intervals, or 0 when none.
Definition: Channel.cpp:124
ChannelGroupData * FindGroupData()
Do not make attachment site on demand if absent.
Definition: Channel.h:553
LinkType
For two tracks describes the type of the linkage.
Definition: Channel.h:571
@ Aligned
Tracks are grouped and changes should be synchronized.
virtual size_t NChannels() const =0
Report the number of channels.
std::unique_ptr< ChannelGroupData > DetachGroupData()
Move attachments out.
Definition: Channel.cpp:101
void Init(const ChannelGroup &other)
Copy, including cloning of attached objects.
Definition: Channel.cpp:89
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:274
Utility to register hooks into a host class that attach client data.
Definition: ClientData.h:228
void ForEach(const Function &function)
Invoke function on each ClientData object that has been created in this.
Definition: ClientData.h:388
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:122
bool GetSelected() const
Selectedness is always the same for all channels of a group.
Definition: Track.cpp:70
virtual void Paste(double t, const Track &src)=0
Weak precondition allows overrides to replicate one channel into many.
Track()
Definition: Track.cpp:39
virtual void SetSelected(bool s)
Definition: Track.cpp:75
bool IsSelected() const
Definition: Track.cpp:288
void Notify(bool allChannels, int code=-1)
Definition: Track.cpp:257
virtual TrackListHolder Duplicate(DuplicateOptions={}) const
public nonvirtual duplication function that invokes Clone()
Definition: Track.cpp:86
virtual ~Track()
Definition: Track.cpp:110
virtual bool SupportsBasicEditing() const
Whether this track type implements cut-copy-paste; by default, true.
Definition: Track.cpp:1213
static const TypeInfo & ClassTypeInfo()
Definition: Track.cpp:1206
bool IsSelectedLeader() const
Definition: Track.cpp:296
virtual void SyncLockAdjust(double oldT1, double newT1)
Definition: Track.cpp:269
TrackNodePointer GetNode() const
Retrieve mNode with debug checks.
Definition: Track.cpp:115
TrackList * GetHolder() const
Definition: Track.h:1480
std::shared_ptr< Track > SubstitutePendingChangedTrack()
Definition: Track.cpp:1164
virtual void DoOnProjectTempoChange(const std::optional< double > &oldTempo, double newTempo)=0
std::shared_ptr< const Track > SubstituteOriginalTrack() const
Definition: Track.cpp:1185
std::shared_ptr< TrackList > GetOwner() const
Definition: Track.h:254
virtual bool LinkConsistencyFix(bool doFix=true)
Check consistency of channel groups, and maybe fix it.
Definition: Track.cpp:299
void SetLinkType(LinkType linkType, bool completeList=true)
Definition: Track.cpp:140
std::shared_ptr< Subclass > SharedPointer()
Definition: Track.h:160
bool HasLinkedTrack() const noexcept
Returns true for leaders of multichannel groups.
Definition: Track.cpp:246
TrackId GetId() const
Definition: Track.h:150
virtual void Clear(double t0, double t1)=0
bool IsLeader() const override
Definition: Track.cpp:291
std::shared_ptr< Track > Holder
Definition: Track.h:225
void SetOwner(const std::weak_ptr< TrackList > &list, TrackNodePointer node)
Update mNode when Track is added to TrackList, or removed from it.
Definition: Track.cpp:122
void SetId(TrackId id)
Definition: Track.h:152
bool HandleCommonXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &valueView)
Definition: Track.cpp:1235
ChannelGroupData & GetGroupData()
Definition: Track.cpp:159
void SetIndex(int index)
Definition: Track.cpp:135
const wxString & GetName() const
Name is always the same for all channels of a group.
Definition: Track.cpp:56
void Init(const Track &orig)
Definition: Track.cpp:50
void OnProjectTempoChange(double newTempo)
method to set project tempo on track
Definition: Track.cpp:1361
void WriteCommonXMLAttributes(XMLWriter &xmlFile, bool includeNameAndSelected=true) const
Definition: Track.cpp:1219
void AdjustPositions()
Definition: Track.cpp:1260
std::weak_ptr< TrackList > mList
Definition: Track.h:139
TrackNodePointer mNode
Holds iterator to self, so that TrackList::Find can be constant-time.
Definition: Track.h:142
virtual TrackListHolder Cut(double t0, double t1)=0
Create tracks and modify this track.
Track * GetLinkedTrack() const
Definition: Track.cpp:220
virtual TrackListHolder Clone(bool backup) const =0
LinkType GetLinkType() const noexcept
Definition: Track.cpp:1280
int mIndex
0-based position of this track in its TrackList
Definition: Track.h:143
virtual std::optional< TranslatableString > GetErrorOpening() const
Definition: Track.cpp:252
const std::optional< double > & GetProjectTempo() const
Definition: Track.cpp:1369
void DoSetLinkType(LinkType linkType, bool completeList=true)
Definition: Track.cpp:174
bool LinkConsistencyCheck()
Do the non-mutating part of consistency fix only and return status.
Definition: Track.h:249
bool Any() const
Definition: Track.cpp:285
void SetName(const wxString &n)
Definition: Track.cpp:61
TrackId mId
Identifies the track only in-session, not persistently.
Definition: Track.h:136
int GetIndex() const
Definition: Track.cpp:130
An in-session identifier of track objects across undo states. It does not persist between sessions.
Definition: Track.h:91
Iterator over only members of a TrackList of the specified subtype, optionally filtered by a predicat...
Definition: Track.h:664
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:993
void PermutationEvent(TrackNodePointer node)
Definition: Track.cpp:480
bool MoveUp(Track *t)
Definition: Track.cpp:929
bool empty() const
Definition: Track.cpp:955
static TrackListHolder Create(AudacityProject *pOwner)
Definition: Track.cpp:365
std::function< void(Track &dest, const Track &src)> Updater
Definition: Track.h:1422
AudacityProject * mOwner
Definition: Track.h:1467
void RegisterPendingNewTracks(TrackList &&list)
Definition: Track.cpp:1460
bool CanMoveUp(Track *t) const
Definition: Track.cpp:856
void AppendOne(TrackList &&list)
Definition: Track.cpp:1470
std::shared_ptr< TrackList > mPendingUpdates
Definition: Track.h:1472
static Track * SwapChannels(Track &track)
Definition: Track.cpp:527
static long sCounter
Definition: Track.h:1417
void Permute(const std::vector< Track * > &tracks)
Definition: Track.cpp:570
void Append(TrackList &&list)
Remove all tracks from list and put them at the end of this
Definition: Track.cpp:1449
double GetEndTime() const
Return the greatest end time of the tracks, or 0 when no tracks.
Definition: Track.cpp:991
bool CanMoveDown(Track *t) const
Definition: Track.cpp:861
std::vector< Updater > mUpdaters
This is in correspondence with leader tracks in mPendingUpdates.
Definition: Track.h:1474
static TrackList * FindUndoTracks(const UndoStackElem &state)
Definition: Track.cpp:1406
void Insert(const Track *before, TrackList &&trackList)
Inserts tracks form trackList starting from position where before is located. If before is nullptr tr...
Definition: Track.cpp:546
void ResizingEvent(TrackNodePointer node)
Definition: Track.cpp:496
TrackNodePointer getBegin() const
Definition: Track.h:1376
size_t NChannels() const
Definition: Track.cpp:960
iterator end()
Definition: Track.h:1051
TrackIter< Track > Find(Track *pTrack)
Definition: Track.cpp:519
wxString MakeUniqueTrackName(const wxString &baseTrackName) const
Returns string that contains baseTrackName, but is guaranteed to be unique among other tracks in that...
Definition: Track.cpp:411
TrackList & operator=(const TrackList &)=delete
TrackKind * Add(const std::shared_ptr< TrackKind > &t)
Definition: Track.h:1201
double GetStartTime() const
Return the least start time of the tracks, or 0 when no tracks.
Definition: Track.cpp:985
bool MoveDown(Track *t)
Definition: Track.cpp:942
size_t Size() const
Definition: Track.h:1259
bool isNull(TrackNodePointer p) const
Definition: Track.h:1369
Track * RegisterPendingChangedTrack(Updater updater, Track *src)
Definition: Track.cpp:998
TrackNodePointer getNext(TrackNodePointer p) const
Move an iterator to the next node, if any; else stay at end.
Definition: Track.h:1381
void Swap(TrackList &that)
Definition: Track.cpp:381
void AdditionEvent(TrackNodePointer node)
Definition: Track.cpp:491
void RecalcPositions(TrackNodePointer node)
Definition: Track.cpp:429
bool MakeMultiChannelTrack(Track &first, int nChannels)
Converts channels to a multichannel track.
Definition: Track.cpp:705
void Remove(Track &track)
Remove a channel group, given the leader.
Definition: Track.cpp:745
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1097
void SelectionEvent(Track &track)
Definition: Track.cpp:460
TrackNodePointer getEnd() const
Definition: Track.h:1373
bool ApplyPendingTracks()
Definition: Track.cpp:1092
TrackListHolder ReplaceOne(Track &t, TrackList &&with)
Definition: Track.cpp:637
TrackIterRange< Track > EmptyRange() const
Definition: Track.cpp:501
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:347
Track * DoAdd(const std::shared_ptr< Track > &t)
Definition: Track.cpp:611
iterator begin()
Definition: Track.h:1050
Track * GetNext(Track *t, bool linked=false) const
Return a track in the list that comes after Track t.
Definition: Track.cpp:801
Track * GetPrev(Track *t, bool linked=false) const
Definition: Track.cpp:820
void ClearPendingTracks(ListOfTracks *pAdded=nullptr)
Definition: Track.cpp:1050
void UpdatePendingTracks()
Definition: Track.cpp:1030
Track * DoAddToHead(const std::shared_ptr< Track > &t)
Definition: Track.cpp:599
Track * FindById(TrackId id)
Definition: Track.cpp:588
std::vector< Track * > UnlinkChannels(Track &track)
Removes linkage if track belongs to a group.
Definition: Track.cpp:691
TrackIter< Track > DoFind(Track *pTrack)
Definition: Track.cpp:511
void DataEvent(const std::shared_ptr< Track > &pTrack, bool allChannels, int code)
Definition: Track.cpp:467
iterator Begin()
This private function still iterates channels not tracks.
Definition: Track.h:1086
iterator End()
This private function still iterates channels not tracks.
Definition: Track.h:1088
AudacityProject * GetOwner()
Definition: Track.h:1032
void QueueEvent(TrackListEvent event)
Definition: Track.cpp:452
void SwapNodes(TrackNodePointer s1, TrackNodePointer s2)
Definition: Track.cpp:870
TrackList(const TrackList &that)=delete
bool HasPendingTracks() const
Definition: Track.cpp:1269
virtual ~TrackList()
Definition: Track.cpp:406
void Clear(bool sendEvent=true)
Make the list empty.
Definition: Track.cpp:772
void DeletionEvent(std::weak_ptr< Track > node, bool duringReplace)
Definition: Track.cpp:485
bool mAssignsIds
Definition: Track.h:1477
TrackNodePointer getPrev(TrackNodePointer p) const
Move an iterator to the previous node, if any; else wrap to end.
Definition: Track.h:1391
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1164
static TrackListHolder Temporary(AudacityProject *pProject, const Track::Holder &left={}, const Track::Holder &right={})
Definition: Track.cpp:1418
Base class for extra information attached to undo/redo states.
Definition: UndoManager.h:83
A view into an attribute value. The class does not take the ownership of the data.
wxString ToWString() const
Convert the view value to wxString.
bool TryGet(bool &value) const noexcept
Try to get a boolean value from the view.
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:25
void WriteAttr(const wxString &name, const Identifier &value)
Definition: XMLWriter.h:36
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:208
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
std::optional< LogWindowUpdater > pUpdater
Definition: LogWindow.cpp:53
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:645
double Accumulate(const TrackList &list, double(Track::*memfn)() const, double ident, const double &(*combine)(const double &, const double &))
Definition: Track.cpp:972
UndoRedoExtensionRegistry::Entry sEntry
Definition: Track.cpp:1399
static RegisteredToolbarFactory factory
std::optional< double > mProjectTempo
Definition: Channel.h:586
Choices when duplicating a track.
Definition: Track.h:300
bool backup
passed to Track::Clone()
Definition: Track.h:306
Empty argument passed to some public constructors.
Definition: Track.h:129
Range between two TrackIters, usable in range-for statements, and with Visit member functions.
Definition: Track.h:825
Notification of changes in individual tracks of TrackList, or of TrackList's composition.
Definition: Track.h:946
@ RESIZING
Posted when some track changed its height.
Definition: Track.h:959
@ SELECTION_CHANGE
Posted when the set of selected tracks changes.
Definition: Track.h:949
@ DELETION
Posted when a track has been deleted from a tracklist. Also posted when one track replaces another.
Definition: Track.h:968
@ ADDITION
Posted when a track has been added to a tracklist. Also posted when one track replaces another.
Definition: Track.h:962
@ PERMUTED
Posted when tracks are reordered but otherwise unchanged.
Definition: Track.h:956
@ TRACK_DATA_CHANGE
Posted when certain fields of a track change.
Definition: Track.h:952
Typically statically constructed.
Definition: UndoManager.h:102
Holds one item with description and time range for the UndoManager.
Definition: UndoManager.h:117
UndoState state
Definition: UndoManager.h:128
Extensions extensions
Definition: UndoManager.h:114
void RestoreUndoRedoState(AudacityProject &project) override
Modify the project when undoing or redoing to some state in history.
Definition: Track.cpp:1387
const std::shared_ptr< TrackList > mpTracks
Definition: Track.cpp:1396
TrackListRestorer(AudacityProject &project)
Definition: Track.cpp:1377
bool CanUndoOrRedo(const AudacityProject &project) override
Whether undo or redo is now permitted; default returns true.
Definition: Track.cpp:1393