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*******************************************************************//*******************************************************************/
23
24#include "Track.h"
25
26#include <algorithm>
27#include <numeric>
28
29#include <float.h>
30#include <wx/file.h>
31#include <wx/textfile.h>
32#include <wx/log.h>
33
34#include "BasicUI.h"
35#include "Project.h"
36
38
39#ifdef _MSC_VER
40//Disable truncation warnings
41#pragma warning( disable : 4786 )
42#endif
43
45: vrulerSize(36,0)
46{
47 mSelected = false;
48
49 mIndex = 0;
50
51 mOffset = 0.0;
52
54}
55
57: vrulerSize( orig.vrulerSize )
58{
59 mIndex = 0;
60 mOffset = orig.mOffset;
61}
62
63// Copy all the track properties except the actual contents
64void Track::Init(const Track &orig)
65{
66 mId = orig.mId;
67
68 mName = orig.mName;
69
70 mSelected = orig.mSelected;
71
72 // Deep copy of any group data
74 std::make_unique<ChannelGroupData>(*orig.mpGroupData) : nullptr;
75
76 mChannel = orig.mChannel;
77}
78
79void Track::SetName( const wxString &n )
80{
81 if ( mName != n ) {
82 mName = n;
83 Notify();
84 }
85}
86
88{
89 if (mSelected != s) {
90 mSelected = s;
91 auto pList = mList.lock();
92 if (pList)
93 pList->SelectionEvent( SharedPointer() );
94 }
95}
96
97void Track::EnsureVisible( bool modifyState )
98{
99 auto pList = mList.lock();
100 if (pList)
101 pList->EnsureVisibleEvent( SharedPointer(), modifyState );
102}
103
104void Track::Merge(const Track &orig)
105{
106 mSelected = orig.mSelected;
107}
108
110{
111 // invoke "virtual constructor" to copy track object proper:
112 auto result = Clone();
113
114 AttachedTrackObjects::ForEach([&](auto &attachment){
115 // Copy view state that might be important to undo/redo
116 attachment.CopyTo( *result );
117 });
118
119 return result;
120}
121
123{
124}
125
126
128{
129 wxASSERT(mList.lock() == NULL || this == mNode.first->get());
130 return mNode;
131}
132
134(const std::weak_ptr<TrackList> &list, TrackNodePointer node)
135{
136 // BUG: When using this function to clear an owner, we may need to clear
137 // focused track too. Otherwise focus could remain on an invisible (or deleted) track.
138 mList = list;
139 mNode = node;
140}
141
143{
144 return mIndex;
145}
146
147void Track::SetIndex(int index)
148{
149 mIndex = index;
150}
151
152void Track::SetLinkType(LinkType linkType, bool completeList)
153{
154 auto pList = mList.lock();
155 if (pList && !pList->mPendingUpdates.empty()) {
156 auto orig = pList->FindById( GetId() );
157 if (orig && orig != this) {
158 orig->SetLinkType(linkType);
159 return;
160 }
161 }
162
163 DoSetLinkType(linkType, completeList);
164
165 if (pList) {
166 pList->RecalcPositions(mNode);
167 pList->ResizingEvent(mNode);
168 }
169}
170
172{
173 if (!mpGroupData)
174 // Make on demand
175 mpGroupData = std::make_unique<ChannelGroupData>();
176 return *mpGroupData;
177}
178
180{
181 auto pTrack = this;
182 if (auto pList = GetOwner())
183 if (auto pLeader = *pList->FindLeader(pTrack))
184 pTrack = pLeader;
185 // May make on demand
186 return pTrack->MakeGroupData();
187}
188
190{
191 // May make group data on demand, but consider that logically const
192 return const_cast<Track *>(this)->GetGroupData();
193}
194
195void Track::DoSetLinkType(LinkType linkType, bool completeList)
196{
197 auto oldType = GetLinkType();
198 if (linkType == oldType)
199 // No change
200 return;
201
202 if (oldType == LinkType::None) {
203 // Becoming linked
204
205 // First ensure there is no partner
206 if (auto partner = GetLinkedTrack())
207 partner->mpGroupData.reset();
208 assert(!GetLinkedTrack());
209
210 // Change the link type
211 MakeGroupData().mLinkType = linkType;
212
213 // If this acquired a partner, it loses any old group data
214 if (auto partner = GetLinkedTrack())
215 partner->mpGroupData.reset();
216 }
217 else if (linkType == LinkType::None) {
218 // Becoming unlinked
219 assert(mpGroupData);
220 if (HasLinkedTrack()) {
221 if (auto partner = GetLinkedTrack()) {
222 // Make independent copy of group data in the partner, which should
223 // have had none
224 assert(!partner->mpGroupData);
225 partner->mpGroupData =
226 std::make_unique<ChannelGroupData>(*mpGroupData);
227 partner->mpGroupData->mLinkType = LinkType::None;
228 }
229 }
230 mpGroupData->mLinkType = LinkType::None;
231 }
232 else {
233 // Remaining linked, changing the type
234 assert(mpGroupData);
235 MakeGroupData().mLinkType = linkType;
236 }
237
238 // Assertion checks only in a debug build, does not have side effects!
239 assert(LinkConsistencyCheck(completeList));
240}
241
243{
244 mChannel = c;
245}
246
248{
249 auto pList = mList.lock();
250 if (!pList)
251 return nullptr;
252
253 if (!pList->isNull(mNode)) {
254 if (HasLinkedTrack()) {
255 auto next = pList->getNext( mNode );
256 if ( !pList->isNull( next ) )
257 return next.first->get();
258 }
259
260 if (mNode.first != mNode.second->begin()) {
261 auto prev = pList->getPrev( mNode );
262 if ( !pList->isNull( prev ) ) {
263 auto track = prev.first->get();
264 if (track && track->HasLinkedTrack())
265 return track;
266 }
267 }
268 }
269
270 return nullptr;
271}
272
273bool Track::HasLinkedTrack() const noexcept
274{
275 return mpGroupData && mpGroupData->mLinkType != LinkType::None;
276}
277
278void Track::Notify( int code )
279{
280 auto pList = mList.lock();
281 if (pList)
282 pList->DataEvent( SharedPointer(), code );
283}
284
285void Track::SyncLockAdjust(double oldT1, double newT1)
286{
287 if (newT1 > oldT1) {
288 // Insert space within the track
289
290 if (oldT1 > GetEndTime())
291 return;
292
293 auto tmp = Cut(oldT1, GetEndTime());
294
295 Paste(newT1, tmp.get());
296 }
297 else if (newT1 < oldT1) {
298 // Remove from the track
299 Clear(newT1, oldT1);
300 }
301}
302
304{
305}
306
308 : Track{ orig, std::move(a) }
309{
310}
311
313{
314}
315
317 const PlayableTrack &orig, ProtectedCreationArg &&a
318) : AudioTrack{ orig, std::move(a) }
319{
320}
321
323{
324 DoSetMute(orig.DoGetMute());
325 DoSetSolo(orig.DoGetSolo());
326 AudioTrack::Init( orig );
327}
328
329void PlayableTrack::Merge( const Track &orig )
330{
331 auto pOrig = dynamic_cast<const PlayableTrack *>(&orig);
332 wxASSERT( pOrig );
333 DoSetMute(pOrig->DoGetMute());
334 DoSetSolo(pOrig->DoGetSolo());
335 AudioTrack::Merge( *pOrig );
336}
337
339{
340 if ( DoGetMute() != m ) {
341 DoSetMute(m);
342 Notify();
343 }
344}
345
347{
348 if ( DoGetSolo() != s ) {
349 DoSetSolo(s);
350 Notify();
351 }
352}
353
355{
356 return mMute.load(std::memory_order_relaxed);
357}
358
360{
361 mMute.store(value, std::memory_order_relaxed);
362}
363
365{
366 return mSolo.load(std::memory_order_relaxed);
367}
368
370{
371 mSolo.store(value, std::memory_order_relaxed);
372}
373
374// Serialize, not with tags of its own, but as attributes within a tag.
376{
377 xmlFile.WriteAttr(wxT("mute"), DoGetMute());
378 xmlFile.WriteAttr(wxT("solo"), DoGetSolo());
380}
381
382// Return true iff the attribute is recognized.
383bool PlayableTrack::HandleXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &value)
384{
385 long nValue;
386
387 if (attr == "mute" && value.TryGet(nValue)) {
388 DoSetMute(nValue != 0);
389 return true;
390 }
391 else if (attr == "solo" && value.TryGet(nValue)) {
392 DoSetSolo(nValue != 0);
393 return true;
394 }
395
396 return AudioTrack::HandleXMLAttribute(attr, value);
397}
398
399bool Track::Any() const
400 { return true; }
401
403 { return GetSelected(); }
404
405bool Track::IsLeader() const
406{
407 return !GetLinkedTrack() || HasLinkedTrack();
408}
409
411 { return IsSelected() && IsLeader(); }
412
414(const Track *n, Track *dest)
415{
416 if (dest) {
417 dest->SetChannel(n->GetChannel());
418 dest->mpGroupData = n->mpGroupData ?
419 std::make_unique<ChannelGroupData>(*n->mpGroupData) : nullptr;
420 dest->SetName(n->GetName());
421 }
422}
423
424bool Track::LinkConsistencyFix(bool doFix, bool completeList)
425{
426 // Sanity checks for linked tracks; unsetting the linked property
427 // doesn't fix the problem, but it likely leaves us with orphaned
428 // sample blocks instead of much worse problems.
429 bool err = false;
430 if (completeList && HasLinkedTrack()) {
431 if (auto link = GetLinkedTrack()) {
432 // A linked track's partner should never itself be linked
433 if (link->HasLinkedTrack()) {
434 err = true;
435 if (doFix) {
436 wxLogWarning(
437 L"Left track %s had linked right track %s with extra right "
438 "track link.\n Removing extra link from right track.",
439 GetName(), link->GetName());
440 link->SetLinkType(LinkType::None);
441 }
442 }
443
444 // Channels should be left and right
445 if ( !( (GetChannel() == Track::LeftChannel &&
446 link->GetChannel() == Track::RightChannel) ||
448 link->GetChannel() == Track::LeftChannel) ) ) {
449 err = true;
450 if (doFix) {
451 wxLogWarning(
452 L"Track %s and %s had left/right track links out of order. "
453 "Setting tracks to not be linked.",
454 GetName(), link->GetName());
456 }
457 }
458 }
459 else {
460 err = true;
461 if (doFix) {
462 wxLogWarning(
463 L"Track %s had link to NULL track. Setting it to not be linked.",
464 GetName());
466 }
467 }
468 }
469 return ! err;
470}
471
472// TrackList
473//
474// The TrackList sends events whenever certain updates occur to the list it
475// is managing. Any other classes that may be interested in get these updates
476// should use TrackList::Subscribe().
477//
478
479// same value as in the default constructed TrackId:
480long TrackList::sCounter = -1;
481
483 [](AudacityProject &project) { return TrackList::Create( &project ); }
484};
485
487{
488 return project.AttachedObjects::Get< TrackList >( key );
489}
490
492{
493 return Get( const_cast< AudacityProject & >( project ) );
494}
495
497 : mOwner{ pOwner }
498{
499}
500
501// Factory function
502std::shared_ptr<TrackList> TrackList::Create( AudacityProject *pOwner )
503{
504 return std::make_shared<TrackList>( pOwner );
505}
506
507#if 0
509{
510 if (this != &that) {
511 this->Clear();
512 Swap(that);
513 }
514 return *this;
515}
516#endif
517
519{
520 auto SwapLOTs = [](
521 ListOfTracks &a, const std::weak_ptr< TrackList > &aSelf,
522 ListOfTracks &b, const std::weak_ptr< TrackList > &bSelf )
523 {
524 a.swap(b);
525 for (auto it = a.begin(), last = a.end(); it != last; ++it)
526 (*it)->SetOwner(aSelf, {it, &a});
527 for (auto it = b.begin(), last = b.end(); it != last; ++it)
528 (*it)->SetOwner(bSelf, {it, &b});
529 };
530
531 const auto self = shared_from_this();
532 const auto otherSelf = that.shared_from_this();
533 SwapLOTs( *this, self, that, otherSelf );
534 SwapLOTs( this->mPendingUpdates, self, that.mPendingUpdates, otherSelf );
535 mUpdaters.swap(that.mUpdaters);
536}
537
539{
540 Clear(false);
541}
542
543wxString TrackList::MakeUniqueTrackName(const wxString& baseTrackName) const
544{
545 int n = 1;
546 while(true)
547 {
548 auto name = wxString::Format("%s %d", baseTrackName, n++);
549
550 bool found {false};
551 for(const auto track : Any())
552 {
553 if(track->GetName() == name)
554 {
555 found = true;
556 break;
557 }
558 }
559 if(!found)
560 return name;
561 }
562}
563
565{
566 if ( isNull( node ) )
567 return;
568
569 Track *t;
570 int i = 0;
571
572 auto prev = getPrev( node );
573 if ( !isNull( prev ) ) {
574 t = prev.first->get();
575 i = t->GetIndex() + 1;
576 }
577
578 const auto theEnd = end();
579 for (auto n = Find( node.first->get() ); n != theEnd; ++n) {
580 t = *n;
581 t->SetIndex(i++);
582 }
583
585}
586
588{
589 BasicUI::CallAfter( [wThis = weak_from_this(), event = std::move(event)]{
590 if (auto pThis = wThis.lock())
591 pThis->Publish(event);
592 } );
593}
594
595void TrackList::SelectionEvent( const std::shared_ptr<Track> &pTrack )
596{
598}
599
600void TrackList::DataEvent( const std::shared_ptr<Track> &pTrack, int code )
601{
602 QueueEvent({
603 TrackListEvent::TRACK_DATA_CHANGE, pTrack, code });
604}
605
607 const std::shared_ptr<Track> &pTrack, bool modifyState )
608{
610 pTrack, static_cast<int>(modifyState) });
611}
612
614{
615 QueueEvent({ TrackListEvent::PERMUTED, *node.first });
616}
617
618void TrackList::DeletionEvent(std::weak_ptr<Track> node, bool duringReplace)
619{
621 { TrackListEvent::DELETION, std::move(node), duringReplace ? 1 : 0 });
622}
623
625{
626 QueueEvent({ TrackListEvent::ADDITION, *node.first });
627}
628
630{
631 QueueEvent({ TrackListEvent::RESIZING, *node.first });
632}
633
636{
637 auto it = const_cast<TrackList*>(this)->getEnd();
638 return {
639 { it, it, it, &Track::Any },
640 { it, it, it, &Track::Any }
641 };
642}
643
646{
647 auto iter = Find(pTrack);
648 while( *iter && ! ( *iter )->IsLeader() )
649 --iter;
650 return iter.Filter( &Track::IsLeader );
651}
652
654{
655 if (!track.HasLinkedTrack())
656 return false;
657 auto pOwner = track.GetOwner();
658 if (!pOwner)
659 return false;
660 auto pPartner = pOwner->GetNext(&track, false);
661 if (!pPartner)
662 return false;
663
664 // Swap channels, avoiding copying of GroupData
665 auto pData = move(track.mpGroupData);
666 assert(pData);
667 pOwner->MoveUp(pPartner);
668 pPartner->mpGroupData = move(pData);
669 pPartner->SetChannel(Track::LeftChannel);
671 return true;
672}
673
674void TrackList::Permute(const std::vector<TrackNodePointer> &permutation)
675{
676 for (const auto iter : permutation) {
677 ListOfTracks::value_type track = *iter.first;
678 erase(iter.first);
679 Track *pTrack = track.get();
680 pTrack->SetOwner(shared_from_this(),
681 { insert(ListOfTracks::end(), track), this });
682 }
683 auto n = getBegin();
686}
687
689{
690 // Linear search. Tracks in a project are usually very few.
691 // Search only the non-pending tracks.
692 auto it = std::find_if( ListOfTracks::begin(), ListOfTracks::end(),
693 [=](const ListOfTracks::value_type &ptr){ return ptr->GetId() == id; } );
694 if (it == ListOfTracks::end())
695 return {};
696 return it->get();
697}
698
699Track *TrackList::DoAddToHead(const std::shared_ptr<Track> &t)
700{
701 Track *pTrack = t.get();
702 push_front(ListOfTracks::value_type(t));
703 auto n = getBegin();
704 pTrack->SetOwner(shared_from_this(), n);
705 pTrack->SetId( TrackId{ ++sCounter } );
707 AdditionEvent(n);
708 return front().get();
709}
710
711Track *TrackList::DoAdd(const std::shared_ptr<Track> &t)
712{
713 push_back(t);
714
715 auto n = getPrev( getEnd() );
716
717 t->SetOwner(shared_from_this(), n);
718 t->SetId( TrackId{ ++sCounter } );
720 AdditionEvent(n);
721 return back().get();
722}
723
724auto TrackList::Replace(Track * t, const ListOfTracks::value_type &with) ->
725 ListOfTracks::value_type
726{
727 ListOfTracks::value_type holder;
728 if (t && with) {
729 auto node = t->GetNode();
730 t->SetOwner({}, {});
731
732 holder = *node.first;
733
734 Track *pTrack = with.get();
735 *node.first = with;
736 pTrack->SetOwner(shared_from_this(), node);
737 pTrack->SetId( t->GetId() );
738 RecalcPositions(node);
739
740 DeletionEvent(t->shared_from_this(), true);
741 AdditionEvent(node);
742 }
743 return holder;
744}
745
747{
748 auto list = track.mList.lock();
749 if (list.get() == this)
750 {
751 auto channels = TrackList::Channels(&track);
752 for (auto c : channels)
753 {
754 c->SetLinkType(Track::LinkType::None);
755 c->SetChannel(Track::ChannelType::MonoChannel);
756 }
757 }
758 else
760}
761
762bool TrackList::MakeMultiChannelTrack(Track& track, int nChannels, bool aligned)
763{
764 if (nChannels != 2)
765 return false;
766
767 auto list = track.mList.lock();
768 if (list.get() == this)
769 {
770 if (*list->FindLeader(&track) != &track)
771 return false;
772
773 auto first = list->Find(&track);
774 auto canLink = [&]() -> bool {
775 int count = nChannels;
776 for (auto it = first, end = TrackList::end(); it != end && count; ++it)
777 {
778 if ((*it)->HasLinkedTrack())
779 return false;
780 --count;
781 }
782 return count == 0;
783 }();
784
785 if (!canLink)
786 return false;
787
788 (*first)->SetChannel(Track::LeftChannel);
789 auto second = std::next(first);
790 (*second)->SetChannel(Track::RightChannel);
791 (*first)->SetLinkType(aligned ? Track::LinkType::Aligned : Track::LinkType::Group);
792 }
793 else
795 return true;
796}
797
799{
800 auto result = getEnd();
801 if (t) {
802 auto node = t->GetNode();
803 t->SetOwner({}, {});
804
805 if ( !isNull( node ) ) {
806 ListOfTracks::value_type holder = *node.first;
807
808 result = getNext( node );
809 erase(node.first);
810 if ( !isNull( result ) )
811 RecalcPositions(result);
812
813 DeletionEvent(t->shared_from_this(), false);
814 }
815 }
816 return result;
817}
818
819void TrackList::Clear(bool sendEvent)
820{
821 // Null out the back-pointers to this in tracks, in case there
822 // are outstanding shared_ptrs to those tracks, making them outlive
823 // the temporary ListOfTracks below.
824 for ( auto pTrack: *this )
825 {
826 pTrack->SetOwner({}, {});
827
828 if (sendEvent)
829 DeletionEvent(pTrack->shared_from_this(), false);
830 }
831
832 for ( auto pTrack: mPendingUpdates )
833 {
834 pTrack->SetOwner({}, {});
835
836 if (sendEvent)
837 DeletionEvent(pTrack, false);
838 }
839
840 ListOfTracks tempList;
841 tempList.swap( *this );
842
843 ListOfTracks updating;
844 updating.swap( mPendingUpdates );
845
846 mUpdaters.clear();
847}
848
850Track *TrackList::GetNext(Track * t, bool linked) const
851{
852 if (t) {
853 auto node = t->GetNode();
854 if ( !isNull( node ) ) {
855 if ( linked && t->HasLinkedTrack() )
856 node = getNext( node );
857
858 if ( !isNull( node ) )
859 node = getNext( node );
860
861 if ( !isNull( node ) )
862 return node.first->get();
863 }
864 }
865
866 return nullptr;
867}
868
869Track *TrackList::GetPrev(Track * t, bool linked) const
870{
871 if (t) {
872 TrackNodePointer prev;
873 auto node = t->GetNode();
874 if ( !isNull( node ) ) {
875 // linked is true and input track second in team?
876 if (linked) {
877 prev = getPrev( node );
878 if( !isNull( prev ) &&
879 !t->HasLinkedTrack() && t->GetLinkedTrack() )
880 // Make it the first
881 node = prev;
882 }
883
884 prev = getPrev( node );
885 if ( !isNull( prev ) ) {
886 // Back up once
887 node = prev;
888
889 // Back up twice sometimes when linked is true
890 if (linked) {
891 prev = getPrev( node );
892 if( !isNull( prev ) &&
893 !(*node.first)->HasLinkedTrack() && (*node.first)->GetLinkedTrack() )
894 node = prev;
895 }
896
897 return node.first->get();
898 }
899 }
900 }
901
902 return nullptr;
903}
904
906{
907 return GetPrev(t, true) != NULL;
908}
909
911{
912 return GetNext(t, true) != NULL;
913}
914
915// This is used when you want to swap the channel group starting
916// at s1 with that starting at s2.
917// The complication is that the tracks are stored in a single
918// linked list.
920{
921 // if a null pointer is passed in, we want to know about it
922 wxASSERT(!isNull(s1));
923 wxASSERT(!isNull(s2));
924
925 // Deal with first track in each team
926 s1 = ( * FindLeader( s1.first->get() ) )->GetNode();
927 s2 = ( * FindLeader( s2.first->get() ) )->GetNode();
928
929 // Safety check...
930 if (s1 == s2)
931 return;
932
933 // Be sure s1 is the earlier iterator
934 if ((*s1.first)->GetIndex() >= (*s2.first)->GetIndex())
935 std::swap(s1, s2);
936
937 // For saving the removed tracks
938 using Saved = std::vector< ListOfTracks::value_type >;
939 Saved saved1, saved2;
940
941 auto doSave = [&] ( Saved &saved, TrackNodePointer &s ) {
942 size_t nn = Channels( s.first->get() ).size();
943 saved.resize( nn );
944 // Save them in backwards order
945 while( nn-- )
946 saved[nn] = *s.first, s.first = erase(s.first);
947 };
948
949 doSave( saved1, s1 );
950 // The two ranges are assumed to be disjoint but might abut
951 const bool same = (s1 == s2);
952 doSave( saved2, s2 );
953 if (same)
954 // Careful, we invalidated s1 in the second doSave!
955 s1 = s2;
956
957 // Reinsert them
958 auto doInsert = [&] ( Saved &saved, TrackNodePointer &s ) {
959 Track *pTrack;
960 for (auto & pointer : saved)
961 pTrack = pointer.get(),
962 // Insert before s, and reassign s to point at the new node before
963 // old s; which is why we saved pointers in backwards order
964 pTrack->SetOwner(shared_from_this(),
965 s = { insert(s.first, pointer), this } );
966 };
967 // This does not invalidate s2 even when it equals s1:
968 doInsert( saved2, s1 );
969 // Even if s2 was same as s1, this correctly inserts the saved1 range
970 // after the saved2 range, when done after:
971 doInsert( saved1, s2 );
972
973 // Now correct the Index in the tracks, and other things
974 RecalcPositions(s1);
976}
977
979{
980 if (t) {
981 Track *p = GetPrev(t, true);
982 if (p) {
983 SwapNodes(p->GetNode(), t->GetNode());
984 return true;
985 }
986 }
987
988 return false;
989}
990
992{
993 if (t) {
994 Track *n = GetNext(t, true);
995 if (n) {
996 SwapNodes(t->GetNode(), n->GetNode());
997 return true;
998 }
999 }
1000
1001 return false;
1002}
1003
1004bool TrackList::Contains(const Track * t) const
1005{
1006 return make_iterator_range( *this ).contains( t );
1007}
1008
1010{
1011 return begin() == end();
1012}
1013
1014size_t TrackList::size() const
1015{
1016 int cnt = 0;
1017
1018 if (!empty())
1019 cnt = getPrev( getEnd() ).first->get()->GetIndex() + 1;
1020
1021 return cnt;
1022}
1023
1024namespace {
1025 // Abstract the common pattern of the following three member functions
1026 inline double Accumulate
1027 (const TrackList &list,
1028 double (Track::*memfn)() const,
1029 double ident,
1030 const double &(*combine)(const double&, const double&))
1031 {
1032 // Default the answer to zero for empty list
1033 if (list.empty()) {
1034 return 0.0;
1035 }
1036
1037 // Otherwise accumulate minimum or maximum of track values
1038 return list.Any().accumulate(ident, combine, memfn);
1039 }
1040}
1041
1043{
1044 return Accumulate(*this, &Track::GetOffset, DBL_MAX, std::min);
1045}
1046
1048{
1049 return Accumulate(*this, &Track::GetStartTime, DBL_MAX, std::min);
1050}
1051
1053{
1054 return Accumulate(*this, &Track::GetEndTime, -DBL_MAX, std::max);
1055}
1056
1057std::shared_ptr<Track>
1059{
1060 std::shared_ptr<Track> pTrack;
1061 if (src) {
1062 pTrack = src->Clone(); // not duplicate
1063 // Share the satellites with the original, though they do not point back
1064 // to the pending track
1065 ((AttachedTrackObjects&)*pTrack) = *src; // shallow copy
1066 }
1067
1068 if (pTrack) {
1069 mUpdaters.push_back( updater );
1070 mPendingUpdates.push_back( pTrack );
1071 auto n = mPendingUpdates.end();
1072 --n;
1073 pTrack->SetOwner(shared_from_this(), {n, &mPendingUpdates});
1074 }
1075
1076 return pTrack;
1077}
1078
1079void TrackList::RegisterPendingNewTrack( const std::shared_ptr<Track> &pTrack )
1080{
1081 Add<Track>( pTrack );
1082 pTrack->SetId( TrackId{} );
1083}
1084
1086{
1087 auto pUpdater = mUpdaters.begin();
1088 for (const auto &pendingTrack : mPendingUpdates) {
1089 // Copy just a part of the track state, according to the update
1090 // function
1091 const auto &updater = *pUpdater;
1092 auto src = FindById( pendingTrack->GetId() );
1093 if (pendingTrack && src) {
1094 if (updater)
1095 updater( *pendingTrack, *src );
1096 pendingTrack->DoSetLinkType(src->GetLinkType());
1097 }
1098 ++pUpdater;
1099 }
1100}
1101
1104{
1105 for (const auto &pTrack: mPendingUpdates)
1106 pTrack->SetOwner( {}, {} );
1107 mPendingUpdates.clear();
1108 mUpdaters.clear();
1109
1110 if (pAdded)
1111 pAdded->clear();
1112
1113 // To find the first node that remains after the first deleted one
1114 TrackNodePointer node;
1115 bool foundNode = false;
1116
1117 for (auto it = ListOfTracks::begin(), stop = ListOfTracks::end();
1118 it != stop;) {
1119 if (it->get()->GetId() == TrackId{}) {
1120 do {
1121 if (pAdded)
1122 pAdded->push_back( *it );
1123 (*it)->SetOwner( {}, {} );
1124 DeletionEvent(*it, false);
1125 it = erase( it );
1126 }
1127 while (it != stop && it->get()->GetId() == TrackId{});
1128
1129 if (!foundNode && it != stop) {
1130 node = (*it)->GetNode();
1131 foundNode = true;
1132 }
1133 }
1134 else
1135 ++it;
1136 }
1137
1138 if (!empty()) {
1140 }
1141}
1142
1145{
1146 bool result = false;
1147
1148 ListOfTracks additions;
1149 ListOfTracks updates;
1150 {
1151 // Always clear, even if one of the update functions throws
1152 auto cleanup = finally( [&] { ClearPendingTracks( &additions ); } );
1154 updates.swap( mPendingUpdates );
1155 }
1156
1157 // Remaining steps must be No-fail-guarantee so that this function
1158 // gives Strong-guarantee
1159
1160 std::vector< std::shared_ptr<Track> > reinstated;
1161
1162 for (auto &pendingTrack : updates) {
1163 if (pendingTrack) {
1164 pendingTrack->AttachedTrackObjects::ForEach([&](auto &attachment){
1165 attachment.Reparent( pendingTrack );
1166 });
1167 auto src = FindById( pendingTrack->GetId() );
1168 if (src)
1169 this->Replace(src, pendingTrack), result = true;
1170 else
1171 // Perhaps a track marked for pending changes got deleted by
1172 // some other action. Recreate it so we don't lose the
1173 // accumulated changes.
1174 reinstated.push_back(pendingTrack);
1175 }
1176 }
1177
1178 // If there are tracks to reinstate, append them to the list.
1179 for (auto &pendingTrack : reinstated)
1180 if (pendingTrack)
1181 this->Add( pendingTrack ), result = true;
1182
1183 // Put the pending added tracks back into the list, preserving their
1184 // positions.
1185 bool inserted = false;
1186 ListOfTracks::iterator first;
1187 for (auto &pendingTrack : additions) {
1188 if (pendingTrack) {
1189 auto iter = ListOfTracks::begin();
1190 std::advance( iter, pendingTrack->GetIndex() );
1191 iter = ListOfTracks::insert( iter, pendingTrack );
1192 pendingTrack->SetOwner( shared_from_this(), {iter, this} );
1193 pendingTrack->SetId( TrackId{ ++sCounter } );
1194 if (!inserted) {
1195 first = iter;
1196 inserted = true;
1197 }
1198 }
1199 }
1200 if (inserted) {
1201 TrackNodePointer node{first, this};
1202 RecalcPositions(node);
1203 AdditionEvent(node);
1204 result = true;
1205 }
1206
1207 return result;
1208}
1209
1211{
1212 // Linear search. Tracks in a project are usually very few.
1213 auto pList = mList.lock();
1214 if (pList) {
1215 const auto id = GetId();
1216 const auto end = pList->mPendingUpdates.end();
1217 auto it = std::find_if(
1218 pList->mPendingUpdates.begin(), end,
1219 [=](const ListOfTracks::value_type &ptr){ return ptr->GetId() == id; } );
1220 if (it != end)
1221 return *it;
1222 }
1223 return SharedPointer();
1224}
1225
1226std::shared_ptr<const Track> Track::SubstitutePendingChangedTrack() const
1227{
1228 return const_cast<Track*>(this)->SubstitutePendingChangedTrack();
1229}
1230
1231std::shared_ptr<const Track> Track::SubstituteOriginalTrack() const
1232{
1233 auto pList = mList.lock();
1234 if (pList) {
1235 const auto id = GetId();
1236 const auto pred = [=]( const ListOfTracks::value_type &ptr ) {
1237 return ptr->GetId() == id; };
1238 const auto end = pList->mPendingUpdates.end();
1239 const auto it = std::find_if( pList->mPendingUpdates.begin(), end, pred );
1240 if (it != end) {
1241 const auto &list2 = (const ListOfTracks &) *pList;
1242 const auto end2 = list2.end();
1243 const auto it2 = std::find_if( list2.begin(), end2, pred );
1244 if ( it2 != end2 )
1245 return *it2;
1246 }
1247 }
1248 return SharedPointer();
1249}
1250
1252{
1253 static Track::TypeInfo info{
1254 { "generic", "generic", XO("Generic Track") }, false };
1255 return info;
1256}
1257
1259{
1260 return true;
1261}
1262
1264{
1265 return {};
1266}
1267
1269{
1270 return {};
1271}
1272
1273// Serialize, not with tags of its own, but as attributes within a tag.
1275 XMLWriter &xmlFile, bool includeNameAndSelected) const
1276{
1277 if (includeNameAndSelected) {
1278 xmlFile.WriteAttr(wxT("name"), GetName());
1279 xmlFile.WriteAttr(wxT("isSelected"), this->GetSelected());
1280 }
1281 AttachedTrackObjects::ForEach([&](auto &attachment){
1282 attachment.WriteXMLAttributes( xmlFile );
1283 });
1284}
1285
1286// Return true iff the attribute is recognized.
1288 const std::string_view& attr, const XMLAttributeValueView& valueView)
1289{
1290 long nValue = -1;
1291
1292 bool handled = false;
1293 AttachedTrackObjects::ForEach([&](auto &attachment){
1294 handled = handled || attachment.HandleXMLAttribute( attr, valueView );
1295 });
1296 if (handled)
1297 ;
1298 else if (attr == "name") {
1299 SetName(valueView.ToWString());
1300 return true;
1301 }
1302 else if (attr == "isSelected" && valueView.TryGet(nValue)) {
1303 this->SetSelected(nValue != 0);
1304 return true;
1305 }
1306 return false;
1307}
1308
1310{
1311 auto pList = mList.lock();
1312 if (pList) {
1313 pList->RecalcPositions(mNode);
1314 pList->ResizingEvent(mNode);
1315 }
1316}
1317
1319{
1320 static Track::TypeInfo info{
1321 { "audio", "audio", XO("Audio Track") },
1322 false, &Track::ClassTypeInfo() };
1323 return info;
1324}
1325
1327{
1328 static Track::TypeInfo info{
1329 { "playable", "playable", XO("Playable Track") },
1330 false, &AudioTrack::ClassTypeInfo() };
1331 return info;
1332}
1333
1335
1337{
1338 if ( !mPendingUpdates.empty() )
1339 return true;
1340 if (end() != std::find_if(begin(), end(), [](const Track *t){
1341 return t->GetId() == TrackId{};
1342 }))
1343 return true;
1344 return false;
1345}
1346
1348{
1349 return mpGroupData ? mpGroupData->mLinkType : LinkType::None;
1350}
1351
1353{
1354 if (auto owner = GetOwner())
1355 {
1356 auto leader = *owner->FindLeader(this);
1357 return leader != this && leader->GetLinkType() == Track::LinkType::Aligned;
1358 }
1359 return false;
1360}
wxT("CloseDown"))
Toolkit-neutral facade for basic user interface services.
int min(int a, int b)
const TranslatableString name
Definition: Distortion.cpp:82
MessageBoxException for violation of preconditions or assertions.
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
#define XO(s)
Definition: Internat.h:31
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: MemoryX.h:431
static CommandHandlerObject & ident(AudacityProject &project)
static const AudacityProject::AttachedObjects::RegisteredFactory key
Definition: Track.cpp:482
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:48
std::list< std::shared_ptr< Track > > ListOfTracks
Definition: Track.h:42
int id
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
Track subclass holding data representing sound (as notes, or samples, or ...)
Definition: Track.h:897
bool HandleXMLAttribute(const std::string_view &, const XMLAttributeValueView &)
Definition: Track.h:908
AudioTrack()
Definition: Track.cpp:303
static const TypeInfo & ClassTypeInfo()
Definition: Track.cpp:1318
void WriteXMLAttributes(XMLWriter &WXUNUSED(xmlFile)) const
Definition: Track.h:905
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
AudioTrack subclass that can also be audibly replayed by the program.
Definition: Track.h:916
bool HandleXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &value)
Definition: Track.cpp:383
std::atomic< bool > mMute
Atomic because it may be read by worker threads in playback.
Definition: Track.h:947
void DoSetSolo(bool value)
Definition: Track.cpp:369
bool DoGetMute() const
Definition: Track.cpp:354
void Init(const PlayableTrack &init)
Definition: Track.cpp:322
void Merge(const Track &init) override
Definition: Track.cpp:329
void DoSetMute(bool value)
Definition: Track.cpp:359
static const TypeInfo & ClassTypeInfo()
Definition: Track.cpp:1326
void WriteXMLAttributes(XMLWriter &xmlFile) const
Definition: Track.cpp:375
void SetSolo(bool s)
Definition: Track.cpp:346
bool DoGetSolo() const
Definition: Track.cpp:364
std::atomic< bool > mSolo
Atomic because it may be read by worker threads in playback.
Definition: Track.h:949
void SetMute(bool m)
Definition: Track.cpp:338
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:225
virtual void Merge(const Track &orig)
Definition: Track.cpp:104
bool GetSelected() const
Definition: Track.h:469
void EnsureVisible(bool modifyState=false)
Definition: Track.cpp:97
Track()
Definition: Track.cpp:44
virtual void SetSelected(bool s)
Definition: Track.cpp:87
virtual void Paste(double WXUNUSED(t), const Track *WXUNUSED(src))=0
virtual Holder Cut(double WXUNUSED(t0), double WXUNUSED(t1))=0
bool IsSelected() const
Definition: Track.cpp:402
virtual void Clear(double WXUNUSED(t0), double WXUNUSED(t1))=0
void SetChannel(ChannelType c) noexcept
Definition: Track.cpp:242
virtual ~Track()
Definition: Track.cpp:122
static void FinishCopy(const Track *n, Track *dest)
Definition: Track.cpp:414
virtual bool SupportsBasicEditing() const
Whether this track type implements cut-copy-paste; by default, true.
Definition: Track.cpp:1258
ChannelType
Definition: Track.h:281
@ LeftChannel
Definition: Track.h:282
@ RightChannel
Definition: Track.h:283
@ MonoChannel
Definition: Track.h:284
virtual double GetStartTime() const =0
static const TypeInfo & ClassTypeInfo()
Definition: Track.cpp:1251
virtual Holder Clone() const =0
bool IsSelectedLeader() const
Definition: Track.cpp:410
virtual void SyncLockAdjust(double oldT1, double newT1)
Definition: Track.cpp:285
double mOffset
Definition: Track.h:447
TrackNodePointer GetNode() const
Retrieve mNode with debug checks.
Definition: Track.cpp:127
virtual bool LinkConsistencyFix(bool doFix=true, bool completeList=true)
Check consistency of channel groups, and maybe fix it.
Definition: Track.cpp:424
ChannelGroupData & GetGroupData()
Definition: Track.cpp:179
std::shared_ptr< Track > SubstitutePendingChangedTrack()
Definition: Track.cpp:1210
std::shared_ptr< const Track > SubstituteOriginalTrack() const
Definition: Track.cpp:1231
std::shared_ptr< TrackList > GetOwner() const
Definition: Track.h:409
bool mSelected
Definition: Track.h:273
void SetLinkType(LinkType linkType, bool completeList=true)
Definition: Track.cpp:152
std::shared_ptr< Subclass > SharedPointer()
Definition: Track.h:297
bool HasLinkedTrack() const noexcept
Returns true for leaders of multichannel groups.
Definition: Track.cpp:273
TrackId GetId() const
Definition: Track.h:287
virtual Holder Duplicate() const
Definition: Track.cpp:109
std::shared_ptr< Track > Holder
Definition: Track.h:368
wxString mName
Definition: Track.h:270
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:134
void Notify(int code=-1)
Definition: Track.cpp:278
void SetId(TrackId id)
Definition: Track.h:289
bool LinkConsistencyCheck(bool completeList)
Do the non-mutating part of consistency fix only and return status.
Definition: Track.h:404
bool HandleCommonXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &valueView)
Definition: Track.cpp:1287
void SetIndex(int index)
Definition: Track.cpp:147
wxString GetName() const
Definition: Track.h:466
void Init(const Track &orig)
Definition: Track.cpp:64
virtual ConstIntervals GetIntervals() const
Report times on the track where important intervals begin and end, for UI to snap to.
Definition: Track.cpp:1263
virtual ChannelType GetChannel() const
Definition: Track.h:479
void WriteCommonXMLAttributes(XMLWriter &xmlFile, bool includeNameAndSelected=true) const
Definition: Track.cpp:1274
void AdjustPositions()
Definition: Track.cpp:1309
std::weak_ptr< TrackList > mList
Definition: Track.h:265
virtual double GetOffset() const =0
TrackNodePointer mNode
Holds iterator to self, so that TrackList::Find can be constant-time.
Definition: Track.h:268
ChannelType mChannel
Definition: Track.h:446
virtual double GetEndTime() const =0
std::vector< Interval > Intervals
Definition: Track.h:335
bool IsLeader() const
Definition: Track.cpp:405
Track * GetLinkedTrack() const
Definition: Track.cpp:247
ChannelGroupData & MakeGroupData()
Definition: Track.cpp:171
LinkType
For two tracks describes the type of the linkage.
Definition: Track.h:236
LinkType GetLinkType() const noexcept
Definition: Track.cpp:1347
int mIndex
0-based position of this track in its TrackList
Definition: Track.h:269
bool IsAlignedWithLeader() const
Returns true if the leader track has link type LinkType::Aligned.
Definition: Track.cpp:1352
void DoSetLinkType(LinkType linkType, bool completeList=true)
Definition: Track.cpp:195
std::unique_ptr< ChannelGroupData > mpGroupData
Definition: Track.h:262
std::vector< ConstInterval > ConstIntervals
Definition: Track.h:337
bool Any() const
Definition: Track.cpp:399
void SetName(const wxString &n)
Definition: Track.cpp:79
TrackId mId
Identifies the track only in-session, not persistently.
Definition: Track.h:260
int GetIndex() const
Definition: Track.cpp:142
An in-session identifier of track objects across undo states. It does not persist between sessions.
Definition: Track.h:151
Iterator over only members of a TrackList of the specified subtype, optionally filtered by a predicat...
Definition: Track.h:1006
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:1338
void PermutationEvent(TrackNodePointer node)
Definition: Track.cpp:613
bool MoveUp(Track *t)
Definition: Track.cpp:978
void RegisterPendingNewTrack(const std::shared_ptr< Track > &pTrack)
Definition: Track.cpp:1079
bool empty() const
Definition: Track.cpp:1009
bool CanMoveUp(Track *t) const
Definition: Track.cpp:905
static long sCounter
Definition: Track.h:1723
bool MakeMultiChannelTrack(Track &first, int nChannels, bool aligned)
Converts channels to a multichannel track.
Definition: Track.cpp:762
static std::shared_ptr< TrackList > Create(AudacityProject *pOwner)
Definition: Track.cpp:502
double GetEndTime() const
Definition: Track.cpp:1052
bool CanMoveDown(Track *t) const
Definition: Track.cpp:910
std::vector< Updater > mUpdaters
This is in correspondence with mPendingUpdates.
Definition: Track.h:1777
double GetMinOffset() const
Definition: Track.cpp:1042
void ResizingEvent(TrackNodePointer node)
Definition: Track.cpp:629
TrackNodePointer getBegin() const
Definition: Track.h:1681
ListOfTracks::value_type Replace(Track *t, const ListOfTracks::value_type &with)
Definition: Track.cpp:724
iterator end()
Definition: Track.h:1391
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:543
TrackList & operator=(const TrackList &)=delete
TrackKind * Add(const std::shared_ptr< TrackKind > &t)
Definition: Track.h:1564
double GetStartTime() const
Definition: Track.cpp:1047
bool MoveDown(Track *t)
Definition: Track.cpp:991
bool isNull(TrackNodePointer p) const
Definition: Track.h:1675
TrackNodePointer getNext(TrackNodePointer p) const
Move an iterator to the next node, if any; else stay at end.
Definition: Track.h:1686
size_t size() const
Definition: Track.cpp:1014
void Swap(TrackList &that)
Definition: Track.cpp:518
void AdditionEvent(TrackNodePointer node)
Definition: Track.cpp:624
void RecalcPositions(TrackNodePointer node)
Definition: Track.cpp:564
auto Find(Track *pTrack) -> TrackIter< TrackType >
Turn a pointer into a TrackIter (constant time); get end iterator if this does not own the track.
Definition: Track.h:1399
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1437
TrackNodePointer getEnd() const
Definition: Track.h:1678
bool ApplyPendingTracks()
Definition: Track.cpp:1144
std::function< void(Track &dest, const Track &src) > Updater
Definition: Track.h:1726
bool Contains(const Track *t) const
Mainly a test function. Uses a linear search, so could be slow.
Definition: Track.cpp:1004
TrackIterRange< Track > EmptyRange() const
Definition: Track.cpp:634
void EnsureVisibleEvent(const std::shared_ptr< Track > &pTrack, bool modifyState)
Definition: Track.cpp:606
void DataEvent(const std::shared_ptr< Track > &pTrack, int code)
Definition: Track.cpp:600
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:486
Track * DoAdd(const std::shared_ptr< Track > &t)
Definition: Track.cpp:711
TrackNodePointer Remove(Track *t)
Remove the Track and return an iterator to what followed it.
Definition: Track.cpp:798
static bool SwapChannels(Track &track)
If the given track is one of a pair of channels, swap them.
Definition: Track.cpp:653
iterator begin()
Definition: Track.h:1390
Track * GetNext(Track *t, bool linked=false) const
Return a track in the list that comes after Track t.
Definition: Track.cpp:850
Track * GetPrev(Track *t, bool linked=false) const
Definition: Track.cpp:869
std::shared_ptr< Track > RegisterPendingChangedTrack(Updater updater, Track *src)
Definition: Track.cpp:1058
void ClearPendingTracks(ListOfTracks *pAdded=nullptr)
Definition: Track.cpp:1103
void UpdatePendingTracks()
Definition: Track.cpp:1085
Track * DoAddToHead(const std::shared_ptr< Track > &t)
Definition: Track.cpp:699
Track * FindById(TrackId id)
Definition: Track.cpp:688
void Permute(const std::vector< TrackNodePointer > &permutation)
For use in sorting: assume each iterator points into this list, no duplications.
Definition: Track.cpp:674
TrackIter< Track > FindLeader(Track *pTrack)
Definition: Track.cpp:644
void QueueEvent(TrackListEvent event)
Definition: Track.cpp:587
void SwapNodes(TrackNodePointer s1, TrackNodePointer s2)
Definition: Track.cpp:919
TrackList(const TrackList &that)=delete
void UnlinkChannels(Track &track)
Removes linkage if track belongs to a group.
Definition: Track.cpp:746
bool HasPendingTracks() const
Definition: Track.cpp:1336
virtual ~TrackList()
Definition: Track.cpp:538
void Clear(bool sendEvent=true)
Make the list empty.
Definition: Track.cpp:819
void DeletionEvent(std::weak_ptr< Track > node, bool duringReplace)
Definition: Track.cpp:618
void SelectionEvent(const std::shared_ptr< Track > &pTrack)
Definition: Track.cpp:595
TrackNodePointer getPrev(TrackNodePointer p) const
Move an iterator to the previous node, if any; else wrap to end.
Definition: Track.h:1696
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1541
ListOfTracks mPendingUpdates
Shadow tracks holding append-recording in progress; need to put them into a list so that GetLink() wo...
Definition: Track.h:1775
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:26
void WriteAttr(const wxString &name, const Identifier &value)
Definition: XMLWriter.h:37
wxString Find(const FilePath &path)
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:206
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:753
double Accumulate(const TrackList &list, double(Track::*memfn)() const, double ident, const double &(*combine)(const double &, const double &))
Definition: Track.cpp:1027
STL namespace.
LinkType mLinkType
Definition: Track.h:252
Empty argument passed to some public constructors.
Definition: Track.h:232
virtual ~TrackIntervalData()
Range between two TrackIters, usable in range-for statements, and with Visit member functions.
Definition: Track.h:1167
Notification of changes in individual tracks of TrackList, or of TrackList's composition.
Definition: Track.h:1288
@ RESIZING
Posted when some track changed its height.
Definition: Track.h:1304
@ SELECTION_CHANGE
Posted when the set of selected tracks changes.
Definition: Track.h:1291
@ DELETION
Posted when a track has been deleted from a tracklist. Also posted when one track replaces another.
Definition: Track.h:1313
@ ADDITION
Posted when a track has been added to a tracklist. Also posted when one track replaces another.
Definition: Track.h:1307
@ PERMUTED
Posted when tracks are reordered but otherwise unchanged.
Definition: Track.h:1301
@ TRACK_REQUEST_VISIBLE
Posted when a track needs to be scrolled into view.
Definition: Track.h:1297
@ TRACK_DATA_CHANGE
Posted when certain fields of a track change.
Definition: Track.h:1294