mirror of
				https://github.com/cookiengineer/audacity
				synced 2025-11-04 08:04:06 +01:00 
			
		
		
		
	Classes and functions for type-safe iteration over tracks
This commit is contained in:
		
							
								
								
									
										112
									
								
								src/Track.cpp
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								src/Track.cpp
									
									
									
									
									
								
							@@ -292,6 +292,26 @@ Track *Track::GetLink() const
 | 
			
		||||
   return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
   inline bool IsSyncLockableNonLabelTrack( const Track *pTrack )
 | 
			
		||||
   {
 | 
			
		||||
      return nullptr != track_cast< const AudioTrack * >( pTrack );
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   bool IsGoodNextSyncLockTrack(const Track *t, bool inLabelSection)
 | 
			
		||||
   {
 | 
			
		||||
      if (!t)
 | 
			
		||||
         return false;
 | 
			
		||||
      const bool isLabel = ( nullptr != track_cast<const LabelTrack*>(t) );
 | 
			
		||||
      if (inLabelSection)
 | 
			
		||||
         return isLabel;
 | 
			
		||||
      else if (isLabel)
 | 
			
		||||
         return true;
 | 
			
		||||
      else
 | 
			
		||||
         return IsSyncLockableNonLabelTrack( t );
 | 
			
		||||
   }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Track::IsSyncLockSelected() const
 | 
			
		||||
{
 | 
			
		||||
#ifdef EXPERIMENTAL_SYNC_LOCK
 | 
			
		||||
@@ -383,6 +403,21 @@ bool PlayableTrack::HandleXMLAttribute(const wxChar *attr, const wxChar *value)
 | 
			
		||||
   return AudioTrack::HandleXMLAttribute(attr, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Track::Any() const
 | 
			
		||||
   { return true; }
 | 
			
		||||
 | 
			
		||||
bool Track::IsSelected() const
 | 
			
		||||
   { return GetSelected(); }
 | 
			
		||||
 | 
			
		||||
bool Track::IsSelectedOrSyncLockSelected() const
 | 
			
		||||
   { return GetSelected() || IsSyncLockSelected(); }
 | 
			
		||||
 | 
			
		||||
bool Track::IsLeader() const
 | 
			
		||||
   { return !GetLink() || GetLinked(); }
 | 
			
		||||
 | 
			
		||||
bool Track::IsSelectedLeader() const
 | 
			
		||||
   { return IsSelected() && IsLeader(); }
 | 
			
		||||
 | 
			
		||||
// TrackListIterator
 | 
			
		||||
TrackListIterator::TrackListIterator(TrackList * val, TrackNodePointer p)
 | 
			
		||||
   : l{ val }
 | 
			
		||||
@@ -652,13 +687,6 @@ SyncLockedTracksIterator::SyncLockedTracksIterator(TrackList * val)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
   inline bool IsSyncLockableNonLabelTrack( const Track *pTrack )
 | 
			
		||||
   {
 | 
			
		||||
      return nullptr != dynamic_cast< const AudioTrack * >( pTrack );
 | 
			
		||||
   }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Track *SyncLockedTracksIterator::StartWith(Track * member)
 | 
			
		||||
{
 | 
			
		||||
   Track *t = NULL;
 | 
			
		||||
@@ -765,10 +793,50 @@ Track *SyncLockedTracksIterator::Last(bool skiplinked)
 | 
			
		||||
         break;
 | 
			
		||||
      t = Next(skiplinked);
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   
 | 
			
		||||
   return t;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<Track *, Track *> TrackList::FindSyncLockGroup(Track *pMember) const
 | 
			
		||||
{
 | 
			
		||||
   if (!pMember)
 | 
			
		||||
      return { nullptr, nullptr };
 | 
			
		||||
 | 
			
		||||
   // A non-trivial sync-locked group is a maximal sub-sequence of the tracks
 | 
			
		||||
   // consisting of any positive number of audio tracks followed by zero or
 | 
			
		||||
   // more label tracks.
 | 
			
		||||
 | 
			
		||||
   // Step back through any label tracks.
 | 
			
		||||
   auto member = pMember;
 | 
			
		||||
   while (member && ( nullptr != track_cast<const LabelTrack*>(member) )) {
 | 
			
		||||
      member = GetPrev(member);
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   // Step back through the wave and note tracks before the label tracks.
 | 
			
		||||
   Track *first = nullptr;
 | 
			
		||||
   while (member && IsSyncLockableNonLabelTrack(member)) {
 | 
			
		||||
      first = member;
 | 
			
		||||
      member = GetPrev(member);
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   if (!first)
 | 
			
		||||
      // Can't meet the criteria described above.  In that case,
 | 
			
		||||
      // consider the track to be the sole member of a group.
 | 
			
		||||
      return { pMember, pMember };
 | 
			
		||||
 | 
			
		||||
   Track *last = first;
 | 
			
		||||
   bool inLabels = false;
 | 
			
		||||
 | 
			
		||||
   while (const auto next = GetNext(last)) {
 | 
			
		||||
      if ( ! IsGoodNextSyncLockTrack(next, inLabels) )
 | 
			
		||||
         break;
 | 
			
		||||
      last = next;
 | 
			
		||||
      inLabels = (nullptr != track_cast<const LabelTrack*>(last) );
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   return { first, last };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// TrackList
 | 
			
		||||
//
 | 
			
		||||
@@ -877,6 +945,34 @@ void TrackList::ResizingEvent(TrackNodePointer node)
 | 
			
		||||
   QueueEvent(e.release());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto TrackList::EmptyRange() const
 | 
			
		||||
   -> TrackIterRange< Track >
 | 
			
		||||
{
 | 
			
		||||
   auto it = const_cast<TrackList*>(this)->getEnd();
 | 
			
		||||
   return {
 | 
			
		||||
      { it, it, it, &Track::Any },
 | 
			
		||||
      { it, it, it, &Track::Any }
 | 
			
		||||
   };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto TrackList::SyncLockGroup( Track *pTrack )
 | 
			
		||||
   -> TrackIterRange< Track >
 | 
			
		||||
{
 | 
			
		||||
   auto pList = pTrack->GetOwner();
 | 
			
		||||
   auto tracks =
 | 
			
		||||
      pList->FindSyncLockGroup( const_cast<Track*>( pTrack ) );
 | 
			
		||||
   return pList->Any().StartingWith(tracks.first).EndingAfter(tracks.second);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto TrackList::FindLeader( Track *pTrack )
 | 
			
		||||
   -> TrackIter< Track >
 | 
			
		||||
{
 | 
			
		||||
   auto iter = Find(pTrack);
 | 
			
		||||
   while( *iter && ! ( *iter )->IsLeader() )
 | 
			
		||||
      --iter;
 | 
			
		||||
   return iter.Filter( &Track::IsLeader );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TrackList::Permute(const std::vector<TrackNodePointer> &permutation)
 | 
			
		||||
{
 | 
			
		||||
   for (const auto iter : permutation) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										462
									
								
								src/Track.h
									
									
									
									
									
								
							
							
						
						
									
										462
									
								
								src/Track.h
									
									
									
									
									
								
							@@ -321,7 +321,9 @@ public:
 | 
			
		||||
 | 
			
		||||
   Track *GetLink() const;
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
private:
 | 
			
		||||
   std::shared_ptr<TrackList> GetOwner() const { return mList.lock(); }
 | 
			
		||||
 | 
			
		||||
   TrackNodePointer GetNode() const;
 | 
			
		||||
   void SetOwner
 | 
			
		||||
      (const std::weak_ptr<TrackList> &list, TrackNodePointer node);
 | 
			
		||||
@@ -454,6 +456,15 @@ public:
 | 
			
		||||
   // Checks if sync-lock is on and any track in its sync-lock group is selected.
 | 
			
		||||
   bool IsSyncLockSelected() const;
 | 
			
		||||
 | 
			
		||||
   // An always-true predicate useful for defining iterators
 | 
			
		||||
   bool Any() const;
 | 
			
		||||
 | 
			
		||||
   // Frequently useful operands for + and -
 | 
			
		||||
   bool IsSelected() const;
 | 
			
		||||
   bool IsSelectedOrSyncLockSelected() const;
 | 
			
		||||
   bool IsLeader() const;
 | 
			
		||||
   bool IsSelectedLeader() const;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
   std::shared_ptr<Track> FindTrack() override;
 | 
			
		||||
 | 
			
		||||
@@ -544,6 +555,247 @@ template<typename T>
 | 
			
		||||
      return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// new track iterators can eliminate the need to cast the result
 | 
			
		||||
template <
 | 
			
		||||
   typename TrackType // Track or a subclass, maybe const-qualified
 | 
			
		||||
> class TrackIter
 | 
			
		||||
   : public std::iterator<
 | 
			
		||||
      std::bidirectional_iterator_tag, TrackType *const
 | 
			
		||||
   >
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
   // Type of predicate taking pointer to const TrackType
 | 
			
		||||
   // TODO C++14:  simplify away ::type
 | 
			
		||||
   using FunctionType = std::function< bool(
 | 
			
		||||
      typename std::add_pointer<
 | 
			
		||||
         typename std::add_const<
 | 
			
		||||
            typename std::remove_pointer<
 | 
			
		||||
               TrackType
 | 
			
		||||
            >::type
 | 
			
		||||
         >::type
 | 
			
		||||
      >::type
 | 
			
		||||
   ) >;
 | 
			
		||||
 | 
			
		||||
   template<typename Predicate = FunctionType>
 | 
			
		||||
   TrackIter( TrackNodePointer begin, TrackNodePointer iter,
 | 
			
		||||
              TrackNodePointer end, const Predicate &pred = {} )
 | 
			
		||||
      : mBegin( begin ), mIter( iter ), mEnd( end ), mPred( pred )
 | 
			
		||||
   {
 | 
			
		||||
      // Establish the class invariant
 | 
			
		||||
      if (this->mIter != this->mEnd && !this->valid())
 | 
			
		||||
         this->operator ++ ();
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   // Return an iterator that replaces the predicate, advancing to the first
 | 
			
		||||
   // position at or after the old position that satisfies the new predicate,
 | 
			
		||||
   // or to the end.
 | 
			
		||||
   template < typename Predicate2 >
 | 
			
		||||
      TrackIter Filter( const Predicate2 &pred2 ) const
 | 
			
		||||
   {
 | 
			
		||||
      return { this->mBegin, this->mIter, this->mEnd, pred2 };
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   // Return an iterator that refines the subclass (and not removing const),
 | 
			
		||||
   // advancing to the first position at or after the old position that
 | 
			
		||||
   // satisfies the type constraint, or to the end
 | 
			
		||||
   template < typename TrackType2 >
 | 
			
		||||
      auto Filter() const
 | 
			
		||||
         -> typename std::enable_if<
 | 
			
		||||
            std::is_base_of< TrackType, TrackType2 >::value &&
 | 
			
		||||
               (!std::is_const<TrackType>::value ||
 | 
			
		||||
                 std::is_const<TrackType2>::value),
 | 
			
		||||
            TrackIter< TrackType2 >
 | 
			
		||||
         >::type
 | 
			
		||||
   {
 | 
			
		||||
      return { this->mBegin, this->mIter, this->mEnd, this->mPred };
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   const FunctionType &GetPredicate() const
 | 
			
		||||
   { return this->mPred; }
 | 
			
		||||
 | 
			
		||||
   // Unlike with STL iterators, this class gives well defined behavior when
 | 
			
		||||
   // you increment an end iterator: you get the same.
 | 
			
		||||
   TrackIter &operator ++ ()
 | 
			
		||||
   {
 | 
			
		||||
      // Maintain the class invariant
 | 
			
		||||
      if (this->mIter != this->mEnd) do
 | 
			
		||||
         ++this->mIter.first;
 | 
			
		||||
      while (this->mIter != this->mEnd && !this->valid() );
 | 
			
		||||
      return *this;
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   TrackIter operator ++ (int)
 | 
			
		||||
   {
 | 
			
		||||
      TrackIter result { *this };
 | 
			
		||||
      this-> operator ++ ();
 | 
			
		||||
      return result;
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   // Unlike with STL iterators, this class gives well defined behavior when
 | 
			
		||||
   // you decrement past the beginning of a range: you wrap and get an end
 | 
			
		||||
   // iterator.
 | 
			
		||||
   TrackIter &operator -- ()
 | 
			
		||||
   {
 | 
			
		||||
      // Maintain the class invariant
 | 
			
		||||
      do {
 | 
			
		||||
         if (this->mIter == this->mBegin)
 | 
			
		||||
            // Go circularly
 | 
			
		||||
            this->mIter = this->mEnd;
 | 
			
		||||
         else
 | 
			
		||||
            --this->mIter.first;
 | 
			
		||||
      } while (this->mIter != this->mEnd && !this->valid() );
 | 
			
		||||
      return *this;
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   TrackIter operator -- (int)
 | 
			
		||||
   {
 | 
			
		||||
      TrackIter result { *this };
 | 
			
		||||
      this->operator -- ();
 | 
			
		||||
      return result;
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   // Unlike with STL iterators, this class gives well defined behavior when
 | 
			
		||||
   // you dereference an end iterator: you get a null pointer.
 | 
			
		||||
   TrackType *operator * () const
 | 
			
		||||
   {
 | 
			
		||||
      if (this->mIter == this->mEnd)
 | 
			
		||||
         return nullptr;
 | 
			
		||||
      else
 | 
			
		||||
         // Other methods guarantee that the cast is correct
 | 
			
		||||
         // (provided no operations on the TrackList invalidated
 | 
			
		||||
         // underlying iterators or replaced the tracks there)
 | 
			
		||||
         return static_cast< TrackType * >( &**this->mIter.first );
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   // This might be called operator + ,
 | 
			
		||||
   // but that might wrongly suggest constant time when the iterator is not
 | 
			
		||||
   // random access.
 | 
			
		||||
   TrackIter advance( long amount ) const
 | 
			
		||||
   {
 | 
			
		||||
      auto copy = *this;
 | 
			
		||||
      std::advance( copy, amount );
 | 
			
		||||
      return copy;
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   friend inline bool operator == (TrackIter a, TrackIter b)
 | 
			
		||||
   {
 | 
			
		||||
      // Assume the predicate is not stateful.  Just compare the iterators.
 | 
			
		||||
      return
 | 
			
		||||
         a.mIter == b.mIter
 | 
			
		||||
         // Assume this too:
 | 
			
		||||
         // && a.mBegin == b.mBegin && a.mEnd == b.mEnd
 | 
			
		||||
      ;
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   friend inline bool operator != (TrackIter a, TrackIter b)
 | 
			
		||||
   {
 | 
			
		||||
      return !(a == b);
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
   bool valid() const
 | 
			
		||||
   {
 | 
			
		||||
      // assume mIter != mEnd
 | 
			
		||||
      const auto pTrack = track_cast< TrackType * >( &**this->mIter.first );
 | 
			
		||||
      if (!pTrack)
 | 
			
		||||
         return false;
 | 
			
		||||
      return !this->mPred || this->mPred( pTrack );
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   // The class invariant is that mIter == mEnd, or else, mIter != mEnd and
 | 
			
		||||
   // **mIter is of the appropriate subclass and mPred(&**mIter) is true.
 | 
			
		||||
   TrackNodePointer mBegin, mIter, mEnd;
 | 
			
		||||
   FunctionType mPred;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <
 | 
			
		||||
   typename TrackType // Track or a subclass, maybe const-qualified
 | 
			
		||||
> struct TrackIterRange
 | 
			
		||||
   : public IteratorRange< TrackIter< TrackType > >
 | 
			
		||||
{
 | 
			
		||||
   TrackIterRange
 | 
			
		||||
      ( const TrackIter< TrackType > &begin,
 | 
			
		||||
        const TrackIter< TrackType > &end )
 | 
			
		||||
         : IteratorRange< TrackIter< TrackType > >
 | 
			
		||||
            ( begin, end )
 | 
			
		||||
   {}
 | 
			
		||||
 | 
			
		||||
   // Conjoin the filter predicate with another predicate
 | 
			
		||||
   // Read + as "and"
 | 
			
		||||
   template< typename Predicate2 >
 | 
			
		||||
      TrackIterRange operator + ( const Predicate2 &pred2 ) const
 | 
			
		||||
   {
 | 
			
		||||
      const auto &pred1 = this->first.GetPredicate();
 | 
			
		||||
      using Function = typename TrackIter<TrackType>::FunctionType;
 | 
			
		||||
      const auto &newPred = pred1
 | 
			
		||||
         ? Function{ [=] (typename Function::argument_type track) {
 | 
			
		||||
            return pred1(track) && pred2(track);
 | 
			
		||||
         } }
 | 
			
		||||
         : Function{ pred2 };
 | 
			
		||||
      return {
 | 
			
		||||
         this->first.Filter( newPred ),
 | 
			
		||||
         this->second.Filter( newPred )
 | 
			
		||||
      };
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   // Specify the added conjunct as a pointer to member function
 | 
			
		||||
   template< typename R, typename C >
 | 
			
		||||
      TrackIterRange operator + ( R ( C ::* pmf ) () const ) const
 | 
			
		||||
   {
 | 
			
		||||
      return this->operator + ( std::mem_fn( pmf ) );
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   // Conjoin the filter predicate with the negation of another predicate
 | 
			
		||||
   // Read - as "and not"
 | 
			
		||||
   template< typename Predicate2 >
 | 
			
		||||
      TrackIterRange operator - ( const Predicate2 &pred2 ) const
 | 
			
		||||
   {
 | 
			
		||||
      using ArgumentType =
 | 
			
		||||
         typename TrackIterRange::iterator::FunctionType::argument_type;
 | 
			
		||||
      auto neg = [=] (ArgumentType track) { return !pred2( track ); };
 | 
			
		||||
      return this->operator + ( neg );
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   // Specify the negated conjunct as a pointer to member function
 | 
			
		||||
   template< typename R, typename C >
 | 
			
		||||
      TrackIterRange operator - ( R ( C ::* pmf ) () const ) const
 | 
			
		||||
   {
 | 
			
		||||
      return this->operator + ( std::not1( std::mem_fn( pmf ) ) );
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   template< typename TrackType2 >
 | 
			
		||||
      TrackIterRange< TrackType2 > Filter() const
 | 
			
		||||
   {
 | 
			
		||||
      return {
 | 
			
		||||
         this-> first.template Filter< TrackType2 >(),
 | 
			
		||||
         this->second.template Filter< TrackType2 >()
 | 
			
		||||
      };
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   TrackIterRange StartingWith( const Track *pTrack ) const
 | 
			
		||||
   {
 | 
			
		||||
      return {
 | 
			
		||||
         this->find( pTrack ),
 | 
			
		||||
         this->second
 | 
			
		||||
      };
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   TrackIterRange EndingAfter( const Track *pTrack ) const
 | 
			
		||||
   {
 | 
			
		||||
      return {
 | 
			
		||||
         this->first,
 | 
			
		||||
         this->reversal().find( pTrack ).base()
 | 
			
		||||
      };
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   // Exclude one given track
 | 
			
		||||
   TrackIterRange Excluding ( const TrackType *pExcluded ) const
 | 
			
		||||
   {
 | 
			
		||||
      return this->operator - (
 | 
			
		||||
         [=](const Track *pTrack){ return pExcluded == pTrack; } );
 | 
			
		||||
   }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AUDACITY_DLL_API TrackListIterator /* not final */
 | 
			
		||||
: public std::iterator< std::forward_iterator_tag, Track *const >
 | 
			
		||||
{
 | 
			
		||||
@@ -789,6 +1041,8 @@ class TrackList final : public wxEvtHandler, public ListOfTracks
 | 
			
		||||
   // Destructor
 | 
			
		||||
   virtual ~TrackList();
 | 
			
		||||
 | 
			
		||||
   // Iteration
 | 
			
		||||
 | 
			
		||||
   // Hide the inherited begin() and end()
 | 
			
		||||
   using iterator = TrackListIterator;
 | 
			
		||||
   using const_iterator = TrackListConstIterator;
 | 
			
		||||
@@ -801,6 +1055,161 @@ class TrackList final : public wxEvtHandler, public ListOfTracks
 | 
			
		||||
   const_iterator cbegin() const { return begin(); }
 | 
			
		||||
   const_iterator cend() const { return end(); }
 | 
			
		||||
 | 
			
		||||
   // Turn a pointer into an iterator (constant time).
 | 
			
		||||
   template < typename TrackType = Track >
 | 
			
		||||
      auto Find(Track *pTrack)
 | 
			
		||||
         -> TrackIter< TrackType >
 | 
			
		||||
   {
 | 
			
		||||
      if (!pTrack || pTrack->GetOwner().get() != this)
 | 
			
		||||
         return EndIterator<TrackType>();
 | 
			
		||||
      else
 | 
			
		||||
         return MakeTrackIterator<TrackType>( pTrack->GetNode() );
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   // Turn a pointer into an iterator (constant time).
 | 
			
		||||
   template < typename TrackType = const Track >
 | 
			
		||||
      auto Find(const Track *pTrack) const
 | 
			
		||||
         -> typename std::enable_if< std::is_const<TrackType>::value,
 | 
			
		||||
            TrackIter< TrackType >
 | 
			
		||||
         >::type
 | 
			
		||||
   {
 | 
			
		||||
      if (!pTrack || pTrack->GetOwner().get() != this)
 | 
			
		||||
         return EndIterator<TrackType>();
 | 
			
		||||
      else
 | 
			
		||||
         return MakeTrackIterator<TrackType>( pTrack->GetNode() );
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   // If the track is not an audio track, or not one of a group of channels,
 | 
			
		||||
   // return the track itself; else return the first channel of its group --
 | 
			
		||||
   // in either case as an iterator that will only visit other leader tracks.
 | 
			
		||||
   // (Generalizing away from the assumption of at most stereo)
 | 
			
		||||
   TrackIter< Track > FindLeader( Track *pTrack );
 | 
			
		||||
 | 
			
		||||
   TrackIter< const Track >
 | 
			
		||||
      FindLeader( const Track *pTrack ) const
 | 
			
		||||
   {
 | 
			
		||||
      return const_cast<TrackList*>(this)->
 | 
			
		||||
         FindLeader( const_cast<Track*>(pTrack) ).Filter< const Track >();
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   template < typename TrackType = Track >
 | 
			
		||||
      auto Any()
 | 
			
		||||
         -> TrackIterRange< TrackType >
 | 
			
		||||
   {
 | 
			
		||||
      return Tracks< TrackType >();
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   template < typename TrackType = const Track >
 | 
			
		||||
      auto Any() const
 | 
			
		||||
         -> typename std::enable_if< std::is_const<TrackType>::value,
 | 
			
		||||
            TrackIterRange< TrackType >
 | 
			
		||||
         >::type
 | 
			
		||||
   {
 | 
			
		||||
      return Tracks< TrackType >();
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   // Abbreviating some frequently used cases
 | 
			
		||||
   template < typename TrackType = Track >
 | 
			
		||||
      auto Selected()
 | 
			
		||||
         -> TrackIterRange< TrackType >
 | 
			
		||||
   {
 | 
			
		||||
      return Tracks< TrackType >( &Track::IsSelected );
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   template < typename TrackType = const Track >
 | 
			
		||||
      auto Selected() const
 | 
			
		||||
         -> typename std::enable_if< std::is_const<TrackType>::value,
 | 
			
		||||
            TrackIterRange< TrackType >
 | 
			
		||||
         >::type
 | 
			
		||||
   {
 | 
			
		||||
      return Tracks< TrackType >( &Track::IsSelected );
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   template < typename TrackType = Track >
 | 
			
		||||
      auto Leaders()
 | 
			
		||||
         -> TrackIterRange< TrackType >
 | 
			
		||||
   {
 | 
			
		||||
      return Tracks< TrackType >( &Track::IsLeader );
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   template < typename TrackType = const Track >
 | 
			
		||||
      auto Leaders() const
 | 
			
		||||
         -> typename std::enable_if< std::is_const<TrackType>::value,
 | 
			
		||||
            TrackIterRange< TrackType >
 | 
			
		||||
         >::type
 | 
			
		||||
   {
 | 
			
		||||
      return Tracks< TrackType >( &Track::IsLeader );
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   template < typename TrackType = Track >
 | 
			
		||||
      auto SelectedLeaders()
 | 
			
		||||
         -> TrackIterRange< TrackType >
 | 
			
		||||
   {
 | 
			
		||||
      return Tracks< TrackType >( &Track::IsSelectedLeader );
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   template < typename TrackType = const Track >
 | 
			
		||||
      auto SelectedLeaders() const
 | 
			
		||||
         -> typename std::enable_if< std::is_const<TrackType>::value,
 | 
			
		||||
            TrackIterRange< TrackType >
 | 
			
		||||
         >::type
 | 
			
		||||
   {
 | 
			
		||||
      return Tracks< TrackType >( &Track::IsSelectedLeader );
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   template<typename TrackType>
 | 
			
		||||
      static auto SingletonRange( TrackType *pTrack )
 | 
			
		||||
         -> TrackIterRange< TrackType >
 | 
			
		||||
   {
 | 
			
		||||
      return pTrack->GetOwner()->template Any<TrackType>()
 | 
			
		||||
         .StartingWith( pTrack ).EndingAfter( pTrack );
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   static TrackIterRange< Track >
 | 
			
		||||
      SyncLockGroup( Track *pTrack );
 | 
			
		||||
 | 
			
		||||
   static TrackIterRange< const Track >
 | 
			
		||||
      SyncLockGroup( const Track *pTrack )
 | 
			
		||||
   {
 | 
			
		||||
      return SyncLockGroup(const_cast<Track*>(pTrack)).Filter<const Track>();
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
   template< typename TrackType, typename InTrackType >
 | 
			
		||||
      static TrackIterRange< TrackType >
 | 
			
		||||
         Channels_( TrackIter< InTrackType > iter1 )
 | 
			
		||||
   {
 | 
			
		||||
      // Assume iterator filters leader tracks
 | 
			
		||||
      if (*iter1) {
 | 
			
		||||
         return {
 | 
			
		||||
            iter1.Filter( &Track::Any )
 | 
			
		||||
               .template Filter<TrackType>(),
 | 
			
		||||
            (++iter1).Filter( &Track::Any )
 | 
			
		||||
               .template Filter<TrackType>()
 | 
			
		||||
         };
 | 
			
		||||
      }
 | 
			
		||||
      else
 | 
			
		||||
         // empty range
 | 
			
		||||
         return {
 | 
			
		||||
            iter1.template Filter<TrackType>(),
 | 
			
		||||
            iter1.template Filter<TrackType>()
 | 
			
		||||
         };
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
   // Find an iterator range of channels including the given track.
 | 
			
		||||
   template< typename TrackType >
 | 
			
		||||
      static auto Channels( TrackType *pTrack )
 | 
			
		||||
         -> TrackIterRange< TrackType >
 | 
			
		||||
   {
 | 
			
		||||
      return Channels_<TrackType>( pTrack->GetOwner()->FindLeader(pTrack) );
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   friend class Track;
 | 
			
		||||
   friend class TrackListIterator;
 | 
			
		||||
   friend class SyncLockedTracksIterator;
 | 
			
		||||
@@ -903,6 +1312,57 @@ class TrackList final : public wxEvtHandler, public ListOfTracks
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
 | 
			
		||||
   // Visit all tracks satisfying a predicate, mutative access
 | 
			
		||||
   template <
 | 
			
		||||
      typename TrackType = Track,
 | 
			
		||||
      typename Pred =
 | 
			
		||||
         typename TrackIterRange< TrackType >::iterator::FunctionType
 | 
			
		||||
   >
 | 
			
		||||
      auto Tracks( const Pred &pred = {} )
 | 
			
		||||
         -> TrackIterRange< TrackType >
 | 
			
		||||
   {
 | 
			
		||||
      auto b = getBegin(), e = getEnd();
 | 
			
		||||
      return { { b, b, e, pred }, { b, e, e, pred } };
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   // Visit all tracks satisfying a predicate, const access
 | 
			
		||||
   template <
 | 
			
		||||
      typename TrackType = const Track,
 | 
			
		||||
      typename Pred =
 | 
			
		||||
         typename TrackIterRange< TrackType >::iterator::FunctionType
 | 
			
		||||
   >
 | 
			
		||||
      auto Tracks( const Pred &pred = {} ) const
 | 
			
		||||
         -> typename std::enable_if< std::is_const<TrackType>::value,
 | 
			
		||||
            TrackIterRange< TrackType >
 | 
			
		||||
         >::type
 | 
			
		||||
   {
 | 
			
		||||
      auto b = const_cast<TrackList*>(this)->getBegin();
 | 
			
		||||
      auto e = const_cast<TrackList*>(this)->getEnd();
 | 
			
		||||
      return { { b, b, e, pred }, { b, e, e, pred } };
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   std::pair<Track *, Track *> FindSyncLockGroup(Track *pMember) const;
 | 
			
		||||
 | 
			
		||||
   template < typename TrackType >
 | 
			
		||||
      TrackIter< TrackType >
 | 
			
		||||
         MakeTrackIterator( TrackNodePointer iter ) const
 | 
			
		||||
   {
 | 
			
		||||
      auto b = const_cast<TrackList*>(this)->getBegin();
 | 
			
		||||
      auto e = const_cast<TrackList*>(this)->getEnd();
 | 
			
		||||
      return { b, iter, e };
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   template < typename TrackType >
 | 
			
		||||
      TrackIter< TrackType >
 | 
			
		||||
         EndIterator() const
 | 
			
		||||
   {
 | 
			
		||||
      auto e = const_cast<TrackList*>(this)->getEnd();
 | 
			
		||||
      return { e, e, e };
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   TrackIterRange< Track > EmptyRange() const;
 | 
			
		||||
 | 
			
		||||
   bool isNull(TrackNodePointer p) const
 | 
			
		||||
   { return (p.second == this && p.first == ListOfTracks::end())
 | 
			
		||||
      || (p.second == &mPendingUpdates && p.first == mPendingUpdates.end()); }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user