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