diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp index 80de1288f..33555e9c9 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp @@ -1344,6 +1344,11 @@ public: bool SyncLocks() override { return true; } + bool MayMigrateTo(Track &other) override + { + return TrackShifter::CommonMayMigrateTo(other); + } + double HintOffsetLarger(double desiredOffset) override { // set it to a sample point, and minimum of 1 sample point diff --git a/src/tracks/ui/TimeShiftHandle.cpp b/src/tracks/ui/TimeShiftHandle.cpp index b9feeec4b..6d2f17960 100644 --- a/src/tracks/ui/TimeShiftHandle.cpp +++ b/src/tracks/ui/TimeShiftHandle.cpp @@ -164,46 +164,6 @@ namespace viewInfo.selectedRegion.t1() ); } - WaveTrack *NthChannel(WaveTrack &leader, int nn) - { - if (nn < 0) - return nullptr; - return *TrackList::Channels( &leader ).begin().advance(nn); - } - - int ChannelPosition(const Track *pChannel) - { - return static_cast( - TrackList::Channels( pChannel ) - .EndingAfter( pChannel ).size() - ) - 1; - } - - // Don't count right channels. - WaveTrack *NthAudioTrack(TrackList &list, int nn) - { - if (nn >= 0) { - for ( auto pTrack : list.Leaders< WaveTrack >() ) - if (nn -- == 0) - return pTrack; - } - - return NULL; - } - - // Don't count right channels. - int TrackPosition(TrackList &list, const Track *pFindTrack) - { - pFindTrack = *list.FindLeader(pFindTrack); - int nn = 0; - for ( auto pTrack : list.Leaders< const WaveTrack >() ) { - if (pTrack == pFindTrack) - return nn; - ++nn; - } - return -1; - } - WaveClip *FindClipAtTime(WaveTrack *pTrack, double time) { if (pTrack) { @@ -279,6 +239,42 @@ double TrackShifter::HintOffsetLarger(double desiredOffset) return desiredOffset; } +bool TrackShifter::MayMigrateTo(Track &) +{ + return false; +} + +bool TrackShifter::CommonMayMigrateTo(Track &otherTrack) +{ + auto &track = GetTrack(); + + // Both tracks need to be owned to decide this + auto pMyList = track.GetOwner().get(); + auto pOtherList = otherTrack.GetOwner().get(); + if (pMyList && pOtherList) { + + // Can migrate to another track of the same kind... + if ( otherTrack.SameKindAs( track ) ) { + + // ... with the same number of channels ... + auto myChannels = TrackList::Channels( &track ); + auto otherChannels = TrackList::Channels( &otherTrack ); + if (myChannels.size() == otherChannels.size()) { + + // ... and where this track and the other have corresponding + // positions + return myChannels.size() == 1 || + std::distance(myChannels.first, pMyList->Find(&track)) == + std::distance(otherChannels.first, pOtherList->Find(&otherTrack)); + + } + + } + + } + return false; +} + void TrackShifter::InitIntervals() { mMoving.clear(); @@ -724,41 +720,62 @@ namespace { } } + using Correspondence = std::unordered_map< Track*, Track* >; + bool FindCorrespondence( + Correspondence &correspondence, TrackList &trackList, Track &track, ClipMoveState &state) { - Track &capturedTrack = *state.mCapturedTrack; - const int diff = - TrackPosition(trackList, &track) - - TrackPosition(trackList, &capturedTrack); - for ( auto &trackClip : state.capturedClipArray ) { - if (trackClip.clip) { - // Move all clips up or down by an equal count of audio tracks. - // Can only move between tracks with equal numbers of channels, - // and among corresponding channels. + auto &capturedTrack = state.mCapturedTrack; + auto sameType = [&]( auto pTrack ){ + return capturedTrack->SameKindAs( *pTrack ); + }; + if (!sameType(&track)) + return false; + + // All tracks of the same kind as the captured track + auto range = trackList.Any() + sameType; - Track *const pSrcTrack = trackClip.track; - auto pDstTrack = NthAudioTrack(trackList, - diff + TrackPosition(trackList, pSrcTrack)); - if (!pDstTrack) + // Find how far this track would shift down among those (signed) + const auto myPosition = + std::distance( range.first, trackList.Find( capturedTrack.get() ) ); + const auto otherPosition = + std::distance( range.first, trackList.Find( &track ) ); + auto diff = otherPosition - myPosition; + + // Point to destination track + auto iter = range.first.advance( diff > 0 ? diff : 0 ); + + for (auto pTrack : range) { + auto &pShifter = state.shifters[pTrack]; + if ( !pShifter->MovingIntervals().empty() ) { + // One of the interesting tracks + + auto pOther = *iter; + if ( diff < 0 || !pOther ) + // No corresponding track return false; - if (TrackList::Channels(pSrcTrack).size() != - TrackList::Channels(pDstTrack).size()) + if ( !pShifter->MayMigrateTo(*pOther) ) + // Rejected for other reason return false; - auto pDstChannel = NthChannel( - *pDstTrack, ChannelPosition(pSrcTrack)); - - if (!pDstChannel) { - wxASSERT(false); - return false; - } - - trackClip.dstTrack = pDstChannel; + correspondence[ pTrack ] = pOther; } + + if ( diff < 0 ) + ++diff; // Still consuming initial tracks + else + ++iter; // Safe to increment TrackIter even at end of range } + + // Record the correspondence in TrackClip + for ( auto &trackClip: state.capturedClipArray ) + if ( trackClip.clip ) + trackClip.dstTrack = + dynamic_cast(correspondence[ trackClip.track ]); + return true; } @@ -841,7 +858,8 @@ bool TimeShiftHandle::DoSlideVertical ClipMoveState &state, TrackList &trackList, Track &dstTrack, double &desiredSlideAmount ) { - if (!FindCorrespondence( trackList, dstTrack, state )) + Correspondence correspondence; + if (!FindCorrespondence( correspondence, trackList, dstTrack, state )) return false; // Having passed that test, remove clips temporarily from their diff --git a/src/tracks/ui/TimeShiftHandle.h b/src/tracks/ui/TimeShiftHandle.h index e3c403512..a7fac692e 100644 --- a/src/tracks/ui/TimeShiftHandle.h +++ b/src/tracks/ui/TimeShiftHandle.h @@ -75,10 +75,19 @@ public: */ virtual double HintOffsetLarger( double desiredOffset ); + //! Whether intervals may migrate to the other track, not yet checking all placement constraints */ + /*! Default implementation returns false */ + virtual bool MayMigrateTo( Track &otherTrack ); + protected: /*! Unfix any of the intervals that intersect the given one; may be useful to override `SelectInterval()` */ void CommonSelectInterval( const TrackInterval &interval ); + /*! May be useful to override `MayMigrateTo()`, if certain other needed overrides are given. + Returns true, iff: tracks have same type, and corresponding positions in their channel groups, + which have same size */ + bool CommonMayMigrateTo( Track &otherTrack ); + //! Derived class constructor can initialize all intervals reported by the track as fixed, none moving /*! This can't be called by the base class constructor, when GetTrack() isn't yet callable */ void InitIntervals();