diff --git a/src/menus/ClipMenus.cpp b/src/menus/ClipMenus.cpp index 8bf81d2cf..2ebe2ec39 100644 --- a/src/menus/ClipMenus.cpp +++ b/src/menus/ClipMenus.cpp @@ -627,10 +627,10 @@ void DoCursorClipBoundary } // This function returns the amount moved. Possibly 0.0. -double DoClipMove - ( ViewInfo &viewInfo, Track *track, +double DoClipMove( AudacityProject &project, Track *track, TrackList &trackList, bool syncLocked, bool right ) { + auto &viewInfo = ViewInfo::Get(project); auto &selectedRegion = viewInfo.selectedRegion; if (track) { @@ -641,9 +641,11 @@ double DoClipMove std::unique_ptr uShifter; // Find the first channel that has a clip at time t0 + auto hitTestResult = TrackShifter::HitTestResult::Track; for (auto channel : TrackList::Channels(track) ) { - uShifter = MakeTrackShifter::Call( *track ); - if( uShifter->HitTest( t0 ) == TrackShifter::HitTestResult::Miss ) + uShifter = MakeTrackShifter::Call( *track, project ); + if ( (hitTestResult = uShifter->HitTest( t0, viewInfo )) == + TrackShifter::HitTestResult::Miss ) uShifter.reset(); else break; @@ -653,7 +655,7 @@ double DoClipMove return 0.0; auto pShifter = uShifter.get(); - state.Init( *track, std::move( uShifter ), + state.Init( project, *track, hitTestResult, std::move( uShifter ), t0, viewInfo, trackList, syncLocked ); auto desiredT0 = viewInfo.OffsetTimeByPixels( t0, ( right ? 1 : -1 ) ); @@ -700,7 +702,7 @@ void DoClipLeftOrRight auto &tracks = TrackList::Get( project ); auto isSyncLocked = settings.IsSyncLocked(); - auto amount = DoClipMove( viewInfo, trackFocus.Get(), + auto amount = DoClipMove( project, trackFocus.Get(), tracks, isSyncLocked, right ); window.ScrollIntoView(selectedRegion.t0()); diff --git a/src/tracks/playabletrack/notetrack/ui/NoteTrackView.cpp b/src/tracks/playabletrack/notetrack/ui/NoteTrackView.cpp index 3af61d5e5..3fd3f8a3c 100644 --- a/src/tracks/playabletrack/notetrack/ui/NoteTrackView.cpp +++ b/src/tracks/playabletrack/notetrack/ui/NoteTrackView.cpp @@ -742,9 +742,16 @@ public: ~NoteTrackShifter() override {} Track &GetTrack() const override { return *mpTrack; } - HitTestResult HitTest( double ) override + HitTestResult HitTest( + double time, const ViewInfo &viewInfo, HitTestParams* ) override { - return HitTestResult::Intervals; + UnfixAll(); + auto t0 = viewInfo.selectedRegion.t0(); + auto t1 = viewInfo.selectedRegion.t1(); + if ( mpTrack->IsSelected() && time >= t0 && time < t1 ) + return HitTestResult::Selection; + else + return HitTestResult::Intervals; } void SelectInterval( const TrackInterval &interval ) override @@ -760,7 +767,7 @@ private: using MakeNoteTrackShifter = MakeTrackShifter::Override; template<> template<> auto MakeNoteTrackShifter::Implementation() -> Function { - return [](NoteTrack &track) { + return [](NoteTrack &track, AudacityProject&) { return std::make_unique(track); }; } diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp index d0c0ab732..f17f11af3 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp @@ -1314,14 +1314,23 @@ public: ~WaveTrackShifter() override {} Track &GetTrack() const override { return *mpTrack; } - HitTestResult HitTest( double time ) override + HitTestResult HitTest( + double time, const ViewInfo &viewInfo, HitTestParams* ) override { auto pClip = mpTrack->GetClipAtTime( time ); if (!pClip) return HitTestResult::Miss; - // Make a side-effect on our intervals + auto t0 = viewInfo.selectedRegion.t0(); + auto t1 = viewInfo.selectedRegion.t1(); + if ( mpTrack->IsSelected() && time >= t0 && time < t1 ) { + // Unfix maybe many intervals (at least one because of test above) + SelectInterval({t0, t1}); + return HitTestResult::Selection; + } + + // Select just one interval UnfixIntervals( [&](const auto &interval){ return static_cast(interval.Extra()) @@ -1458,7 +1467,7 @@ private: using MakeWaveTrackShifter = MakeTrackShifter::Override; template<> template<> auto MakeWaveTrackShifter::Implementation() -> Function { - return [](WaveTrack &track) { + return [](WaveTrack &track, AudacityProject&) { return std::make_unique(track); }; } diff --git a/src/tracks/ui/TimeShiftHandle.cpp b/src/tracks/ui/TimeShiftHandle.cpp index e7a59a40a..80f588475 100644 --- a/src/tracks/ui/TimeShiftHandle.cpp +++ b/src/tracks/ui/TimeShiftHandle.cpp @@ -241,7 +241,8 @@ CoarseTrackShifter::CoarseTrackShifter( Track &track ) CoarseTrackShifter::~CoarseTrackShifter() = default; -auto CoarseTrackShifter::HitTest( double ) -> HitTestResult +auto CoarseTrackShifter::HitTest( + double, const ViewInfo&, HitTestParams* ) -> HitTestResult { return HitTestResult::Track; } @@ -252,13 +253,15 @@ bool CoarseTrackShifter::SyncLocks() } template<> auto MakeTrackShifter::Implementation() -> Function { - return [](Track &track) { + return [](Track &track, AudacityProject&) { return std::make_unique(track); }; } void ClipMoveState::Init( + AudacityProject &project, Track &capturedTrack, + TrackShifter::HitTestResult hitTestResult, std::unique_ptr pHit, double clickTime, const ViewInfo &viewInfo, @@ -269,27 +272,35 @@ void ClipMoveState::Init( auto &state = *this; state.mCapturedTrack = capturedTrack.SharedPointer(); - state.movingSelection = capturedTrack.IsSelected() && - clickTime >= viewInfo.selectedRegion.t0() && - clickTime < viewInfo.selectedRegion.t1(); + switch (hitTestResult) { + case TrackShifter::HitTestResult::Miss: + wxASSERT(false); + pHit.reset(); + break; + case TrackShifter::HitTestResult::Track: + pHit.reset(); + break; + case TrackShifter::HitTestResult::Intervals: + break; + case TrackShifter::HitTestResult::Selection: + state.movingSelection = true; + break; + default: + break; + } if (!pHit) return; - const bool capturedAClip = - pHit && !pHit->MovingIntervals().empty(); - state.shifters[&capturedTrack] = std::move( pHit ); // Collect TrackShifters for the rest of the tracks for ( auto track : trackList.Any() ) { auto &pShifter = state.shifters[track]; if (!pShifter) - pShifter = MakeTrackShifter::Call( *track ); + pShifter = MakeTrackShifter::Call( *track, project ); } - // Analogy of the steps above, but with TrackShifters, follows below - if ( state.movingSelection ) { // All selected tracks may move some intervals const TrackInterval interval{ @@ -299,6 +310,9 @@ void ClipMoveState::Init( for ( const auto &pair : state.shifters ) { auto &shifter = *pair.second; auto &track = shifter.GetTrack(); + if (&track == &capturedTrack) + // Don't change the choice of intervals made by HitTest + continue; if ( track.IsSelected() ) shifter.SelectInterval( interval ); } @@ -307,12 +321,8 @@ void ClipMoveState::Init( // Move intervals only of the chosen channel group for ( auto channel : TrackList::Channels( &capturedTrack ) ) { auto &shifter = *state.shifters[channel]; - if ( capturedAClip ) { - if ( channel != &capturedTrack ) - shifter.SelectInterval(TrackInterval{clickTime, clickTime}); - } - else - shifter.UnfixAll(); + if ( channel != &capturedTrack ) + shifter.SelectInterval(TrackInterval{clickTime, clickTime}); } } @@ -457,29 +467,28 @@ UIHandle::Result TimeShiftHandle::Click const double clickTime = viewInfo.PositionToTime(event.m_x, rect.x); - bool captureClips = false; - - auto pShifter = MakeTrackShifter::Call( *pTrack ); + auto pShifter = MakeTrackShifter::Call( *pTrack, *pProject ); + auto hitTestResult = TrackShifter::HitTestResult::Track; if (!event.ShiftDown()) { - switch( pShifter->HitTest( clickTime ) ) { + TrackShifter::HitTestParams params{ + rect, event.m_x, event.m_y + }; + hitTestResult = pShifter->HitTest( clickTime, viewInfo, ¶ms ); + switch( hitTestResult ) { case TrackShifter::HitTestResult::Miss: return Cancelled; - case TrackShifter::HitTestResult::Intervals: { - captureClips = true; - break; - } - case TrackShifter::HitTestResult::Track: default: break; } } else { - // As in the default above: just do shifting of one whole track + // just do shifting of one whole track } - mClipMoveState.Init( *pTrack, - captureClips ? std::move( pShifter ) : nullptr, + mClipMoveState.Init( *pProject, *pTrack, + hitTestResult, + std::move( pShifter ), clickTime, viewInfo, trackList, diff --git a/src/tracks/ui/TimeShiftHandle.h b/src/tracks/ui/TimeShiftHandle.h index b81af77ee..7121be8c5 100644 --- a/src/tracks/ui/TimeShiftHandle.h +++ b/src/tracks/ui/TimeShiftHandle.h @@ -25,6 +25,8 @@ class TrackList; class Track; class TrackInterval; +class ViewInfo; + //! Abstract base class for policies to manipulate a track type with the Time Shift tool class TrackShifter { public: @@ -35,13 +37,28 @@ public: //! Possibilities for HitTest on the clicked track enum class HitTestResult { Miss, //!< Don't shift anything - Intervals, //viewInfo.PositionToTime(pParams->xx, pParams->rect.x))` + */ + virtual HitTestResult HitTest( + double time, //!< A time value to test + const ViewInfo &viewInfo, + HitTestParams *pParams = nullptr //!< Optional extra information + ) = 0; using Intervals = std::vector; @@ -158,7 +175,7 @@ public: ~CoarseTrackShifter() override; Track &GetTrack() const override { return *mpTrack; } - HitTestResult HitTest( double ) override; + HitTestResult HitTest( double, const ViewInfo&, HitTestParams* ) override; //! Returns false bool SyncLocks() override; @@ -169,7 +186,7 @@ private: struct MakeTrackShifterTag; using MakeTrackShifter = AttachedVirtualFunction< - MakeTrackShifterTag, std::unique_ptr, Track>; + MakeTrackShifterTag, std::unique_ptr, Track, AudacityProject&>; class ViewInfo; @@ -178,9 +195,11 @@ struct ClipMoveState { //! Will associate a TrackShifter with each track in the list void Init( + AudacityProject &project, Track &capturedTrack, // pHit, /*!< - If null, only capturedTrack (with any sister channels) shifts, as a whole */ + If null, implies `Track`, overriding previous argument */ double clickTime, const ViewInfo &viewInfo, TrackList &trackList, bool syncLocked );