From a7a4a70e5ff1614a4158a079e7e126b99a85c4b6 Mon Sep 17 00:00:00 2001 From: David Bailes Date: Sat, 4 Mar 2017 12:12:17 +0000 Subject: [PATCH] Two commands added, neither on a menu: Clip Left Clip Right If the cursor lies within a clip, the clip and the cursor is moved 1 pixel left/right. If the cursor position is at both a clip end and a clip start, the second of these clips is moved. The movement currently ignores the snap to setting on the selection bar, and there is no snapping to the clip boundaries of other clips. Following the behaviour or shifting with the mouse, the distance moved is rounded to an integral number of samples, and the minimum distance moved is one sample. --- src/Menus.cpp | 13 +++ src/Menus.h | 3 + src/TrackPanel.cpp | 229 ++++++++++++++++++++++++++++----------------- src/TrackPanel.h | 4 + src/WaveTrack.cpp | 11 +++ src/WaveTrack.h | 1 + 6 files changed, 177 insertions(+), 84 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index 4e4944d19..c516096fb 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -1276,6 +1276,9 @@ void AudacityProject::CreateMenusAndCommands() c->AddCommand(wxT("CursorLongJumpLeft"), _("Cursor Long Jump Left"), FN(OnCursorLongJumpLeft), wxT("Shift+,")); c->AddCommand(wxT("CursorLongJumpRight"), _("Cursor Long Jump Right"), FN(OnCursorLongJumpRight), wxT("Shift+.")); + c->AddCommand(wxT("ClipLeft"), _("Clip Left"), FN(OnClipLeft), wxT("")); + c->AddCommand(wxT("ClipRight"), _("Clip Right"), FN(OnClipRight), wxT("")); + c->AddCommand(wxT("SelExtLeft"), _("Selection Extend Left"), FN(OnSelExtendLeft), wxT("Shift+Left\twantKeyup\tallowDup")); c->AddCommand(wxT("SelExtRight"), _("Selection Extend Right"), FN(OnSelExtendRight), wxT("Shift+Right\twantKeyup\tallowDup")); @@ -2930,6 +2933,16 @@ void AudacityProject::OnSelContractRight(const wxEvent * evt) OnCursorLeft( true, true, evt->GetEventType() == wxEVT_KEY_UP ); } +void AudacityProject::OnClipLeft() +{ + mTrackPanel->OnClipMove(false); +} + +void AudacityProject::OnClipRight() +{ + mTrackPanel->OnClipMove(true); +} + //this pops up a dialog which allows the left selection to be set. //If playing/recording is happening, it sets the left selection at //the current play position. diff --git a/src/Menus.h b/src/Menus.h index 83a9ec719..4ce144273 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -160,6 +160,9 @@ void OnSelExtendRight(const wxEvent * evt); void OnSelContractLeft(const wxEvent * evt); void OnSelContractRight(const wxEvent * evt); +void OnClipLeft(); +void OnClipRight(); + void OnCursorShortJumpLeft(); void OnCursorShortJumpRight(); void OnCursorLongJumpLeft(); diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index 9a6d61b31..f13f7be1e 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -3479,89 +3479,18 @@ void TrackPanel::StartSlide(wxMouseEvent & event) if (mCapturedClip == NULL) return; } - // The captured clip is the focus, but we need to create a list - // of all clips that have to move, also... - mCapturedClipArray.clear(); - - // First, if click was in selection, capture selected clips; otherwise - // just the clicked-on clip - if (mCapturedClipIsSelection) { - TrackListIterator iter(GetTracks()); - for (Track *t = iter.First(); t; t = iter.Next()) { - if (t->GetSelected()) { - AddClipsToCaptured(t, true); - if (t->GetKind() != Track::Wave) - mTrackExclusions.push_back(t); - } - } - } - else { - mCapturedClipArray.push_back(TrackClip(vt, mCapturedClip)); - - // Check for stereo partner - Track *partner = vt->GetLink(); - WaveTrack *wt; - if (mCapturedClip && - // Assume linked track is wave or null - nullptr != (wt = static_cast(partner))) { - WaveClip *const clip = - FindClipAtTime(wt, - mViewInfo->PositionToTime(event.m_x, GetLeftOffset())); - if (clip) - mCapturedClipArray.push_back(TrackClip(partner, clip)); - } - } - - // Now, if sync-lock is enabled, capture any clip that's linked to a - // captured clip. - if (GetProject()->IsSyncLocked()) { - // AWD: mCapturedClipArray expands as the loop runs, so newly-added - // clips are considered (the effect is like recursion and terminates - // because AddClipsToCaptured doesn't add duplicate clips); to remove - // this behavior just store the array size beforehand. - for (unsigned int i = 0; i < mCapturedClipArray.size(); ++i) { - // Capture based on tracks that have clips -- that means we - // don't capture based on links to label tracks for now (until - // we can treat individual labels as clips) - if (mCapturedClipArray[i].clip) { - // Iterate over sync-lock group tracks. - SyncLockedTracksIterator git(GetTracks()); - for (Track *t = git.StartWith(mCapturedClipArray[i].track); - t; t = git.Next() ) - { - AddClipsToCaptured(t, - mCapturedClipArray[i].clip->GetStartTime(), - mCapturedClipArray[i].clip->GetEndTime() ); - if (t->GetKind() != Track::Wave) - mTrackExclusions.push_back(t); - } - } -#ifdef USE_MIDI - // Capture additional clips from NoteTracks - Track *nt = mCapturedClipArray[i].track; - if (nt->GetKind() == Track::Note) { - // Iterate over sync-lock group tracks. - SyncLockedTracksIterator git(GetTracks()); - for (Track *t = git.StartWith(nt); t; t = git.Next()) - { - AddClipsToCaptured(t, nt->GetStartTime(), nt->GetEndTime()); - if (t->GetKind() != Track::Wave) - mTrackExclusions.push_back(t); - } - } -#endif - } - } + mCapturedTrack = vt; + CreateListOfCapturedClips(clickTime); } else { mCapturedClip = NULL; mCapturedClipArray.clear(); + mCapturedTrack = vt; } mSlideUpDownOnly = event.CmdDown() && !multiToolModeActive; - mCapturedTrack = vt; mCapturedRect = rect; mMouseClickX = event.m_x; @@ -3587,6 +3516,83 @@ void TrackPanel::StartSlide(wxMouseEvent & event) mMouseCapture = IsSliding; } +void TrackPanel::CreateListOfCapturedClips(double clickTime) +{ +// The captured clip is the focus, but we need to create a list + // of all clips that have to move, also... + + mCapturedClipArray.clear(); + + // First, if click was in selection, capture selected clips; otherwise + // just the clicked-on clip + if (mCapturedClipIsSelection) { + TrackListIterator iter(GetTracks()); + for (Track *t = iter.First(); t; t = iter.Next()) { + if (t->GetSelected()) { + AddClipsToCaptured(t, true); + if (t->GetKind() != Track::Wave) + mTrackExclusions.push_back(t); + } + } + } + else { + mCapturedClipArray.push_back(TrackClip(mCapturedTrack, mCapturedClip)); + + // Check for stereo partner + Track *partner = mCapturedTrack->GetLink(); + WaveTrack *wt; + if (mCapturedClip && + // Assume linked track is wave or null + nullptr != (wt = static_cast(partner))) { + WaveClip *const clip = FindClipAtTime(wt, clickTime); + + if (clip) + mCapturedClipArray.push_back(TrackClip(partner, clip)); + } + } + + // Now, if sync-lock is enabled, capture any clip that's linked to a + // captured clip. + if (GetProject()->IsSyncLocked()) { + // AWD: mCapturedClipArray expands as the loop runs, so newly-added + // clips are considered (the effect is like recursion and terminates + // because AddClipsToCaptured doesn't add duplicate clips); to remove + // this behavior just store the array size beforehand. + for (unsigned int i = 0; i < mCapturedClipArray.size(); ++i) { + // Capture based on tracks that have clips -- that means we + // don't capture based on links to label tracks for now (until + // we can treat individual labels as clips) + if (mCapturedClipArray[i].clip) { + // Iterate over sync-lock group tracks. + SyncLockedTracksIterator git(GetTracks()); + for (Track *t = git.StartWith(mCapturedClipArray[i].track); + t; t = git.Next() ) + { + AddClipsToCaptured(t, + mCapturedClipArray[i].clip->GetStartTime(), + mCapturedClipArray[i].clip->GetEndTime() ); + if (t->GetKind() != Track::Wave) + mTrackExclusions.push_back(t); + } + } +#ifdef USE_MIDI + // Capture additional clips from NoteTracks + Track *nt = mCapturedClipArray[i].track; + if (nt->GetKind() == Track::Note) { + // Iterate over sync-lock group tracks. + SyncLockedTracksIterator git(GetTracks()); + for (Track *t = git.StartWith(nt); t; t = git.Next()) + { + AddClipsToCaptured(t, nt->GetStartTime(), nt->GetEndTime()); + if (t->GetKind() != Track::Wave) + mTrackExclusions.push_back(t); + } + } +#endif + } + } +} + // Helper for the above, adds a track's clips to mCapturedClipArray (eliminates // duplication of this logic) void TrackPanel::AddClipsToCaptured(Track *t, bool withinSelection) @@ -3877,6 +3883,24 @@ void TrackPanel::DoSlide(wxMouseEvent & event) mHSlideAmount = desiredSlideAmount; + DoSlideHorizontal(); + + + if (mCapturedClipIsSelection) { + // Slide the selection, too + mViewInfo->selectedRegion.move(mHSlideAmount); + } + + if (slidVertically) { + // NEW origin + mHSlideAmount = 0; + } + + Refresh(false); +} + +void TrackPanel::DoSlideHorizontal() +{ #ifdef USE_MIDI if (mCapturedClipArray.size()) #else @@ -3947,18 +3971,55 @@ void TrackPanel::DoSlide(wxMouseEvent & event) if (link) link->Offset(mHSlideAmount); } +} - if (mCapturedClipIsSelection) { - // Slide the selection, too - mViewInfo->selectedRegion.move(mHSlideAmount); +void TrackPanel::OnClipMove(bool right) +{ + auto track = GetFocusedTrack(); + + + // just dealing with clips in wave tracks for the moment. Note tracks?? + if (track && track->GetKind() == Track::Wave) { + auto wt = static_cast(track); + mCapturedClip = wt->GetClipAtTime(mViewInfo->selectedRegion.t0()); + if (mCapturedClip == nullptr) + return; + + mCapturedTrack = track; + mCapturedClipIsSelection = track->GetSelected() && !mViewInfo->selectedRegion.isPoint(); + mTrackExclusions.clear(); + + CreateListOfCapturedClips(mViewInfo->selectedRegion.t0()); + + double desiredSlideAmount = mViewInfo->OffsetTimeByPixels(0.0, 1); + + // set it to a sample point, and minimum of 1 sample point + double nSamples = rint(wt->GetRate() * desiredSlideAmount); + nSamples = std::max(nSamples, 1.0); + desiredSlideAmount = nSamples / wt->GetRate(); + + if (!right) + desiredSlideAmount *= -1; + mHSlideAmount = desiredSlideAmount; + DoSlideHorizontal(); + + // update t0 and t1. There is the possibility that the updated + // t0 may no longer be within the clip due to rounding errors, + // so t0 is adjusted so that it is. + double newT0 = mViewInfo->selectedRegion.t0() + mHSlideAmount; + if (newT0 < mCapturedClip->GetStartTime()) + newT0 = mCapturedClip->GetStartTime(); + if (newT0 > mCapturedClip->GetEndTime()) + newT0 = mCapturedClip->GetEndTime(); + double diff = mViewInfo->selectedRegion.t1() - mViewInfo->selectedRegion.t0(); + mViewInfo->selectedRegion.setTimes(newT0, newT0 + diff); + + ScrollIntoView(mViewInfo->selectedRegion.t0()); + Refresh(false); + + if (mHSlideAmount == 0.0) + MessageForScreenReader( _("clip not moved")); } - - if (slidVertically) { - // NEW origin - mHSlideAmount = 0; - } - - Refresh(false); } diff --git a/src/TrackPanel.h b/src/TrackPanel.h index a0f444033..10d38c629 100644 --- a/src/TrackPanel.h +++ b/src/TrackPanel.h @@ -252,6 +252,8 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel { // (ignoring any fisheye) virtual double GetScreenEndTime() const; + virtual void OnClipMove(bool right); + protected: virtual MixerBoard* GetMixerBoard(); /** @brief Populates the track pop-down menu with the common set of @@ -377,6 +379,8 @@ protected: virtual void HandleSlide(wxMouseEvent & event); virtual void StartSlide(wxMouseEvent &event); virtual void DoSlide(wxMouseEvent &event); + virtual void DoSlideHorizontal(); + virtual void CreateListOfCapturedClips(double clickTime); virtual void AddClipsToCaptured(Track *t, bool withinSelection); virtual void AddClipsToCaptured(Track *t, double t0, double t1); diff --git a/src/WaveTrack.cpp b/src/WaveTrack.cpp index bc1a3bffd..ffc12a8c4 100644 --- a/src/WaveTrack.cpp +++ b/src/WaveTrack.cpp @@ -2228,6 +2228,17 @@ WaveClip* WaveTrack::GetClipAtSample(sampleCount sample) return NULL; } +WaveClip* WaveTrack::GetClipAtTime(double time) +{ + // When the time is both the end of a clip and the start of the next clip, the + // latter clip is returned. + const auto clips = SortedClipArray(); + auto result = find_if(clips.rbegin(), clips.rend(), [&] (WaveClip* const& clip) { + return time >= clip->GetStartTime() && time <= clip->GetEndTime(); }); + + return result != clips.rend() ? *result : nullptr; +} + Envelope* WaveTrack::GetEnvelopeAtX(int xcoord) { WaveClip* clip = GetClipAtX(xcoord); diff --git a/src/WaveTrack.h b/src/WaveTrack.h index a5e59873b..12e0006b1 100644 --- a/src/WaveTrack.h +++ b/src/WaveTrack.h @@ -276,6 +276,7 @@ class AUDACITY_DLL_API WaveTrack final : public PlayableTrack { Envelope* GetEnvelopeAtX(int xcoord); WaveClip* GetClipAtSample(sampleCount sample); + WaveClip* GetClipAtTime(double time); // // Getting information about the track's internal block sizes