1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-16 16:10:06 +02:00

Move more time-shifting functions out of TrackPanel.cpp

This commit is contained in:
Paul Licameli 2017-06-01 21:54:22 -04:00
parent 251976d93d
commit ef38af71dd
6 changed files with 385 additions and 404 deletions

View File

@ -3358,6 +3358,62 @@ void AudacityProject::OnSelContractRight(const wxEvent * evt)
OnCursorLeft( true, true, bKeyUp );
}
#include "tracks/ui/TimeShiftHandle.h"
// This function returns the amount moved. Possibly 0.0.
double AudacityProject::OnClipMove
( ViewInfo &viewInfo, Track *track,
TrackList &trackList, bool syncLocked, bool right )
{
// just dealing with clips in wave tracks for the moment. Note tracks??
if (track && track->GetKind() == Track::Wave) {
ClipMoveState state;
auto wt = static_cast<WaveTrack*>(track);
auto t0 = viewInfo.selectedRegion.t0();
state.capturedClip = wt->GetClipAtTime( t0 );
if (state.capturedClip == nullptr)
return 0.0;
state.capturedClipIsSelection =
track->GetSelected() && !viewInfo.selectedRegion.isPoint();
state.trackExclusions.clear();
TimeShiftHandle::CreateListOfCapturedClips
( state, viewInfo, *track, trackList, syncLocked, t0 );
auto desiredT0 = viewInfo.OffsetTimeByPixels( t0, ( right ? 1 : -1 ) );
auto desiredSlideAmount = desiredT0 - t0;
// set it to a sample point, and minimum of 1 sample point
if (!right)
desiredSlideAmount *= -1;
double nSamples = rint(wt->GetRate() * desiredSlideAmount);
nSamples = std::max(nSamples, 1.0);
desiredSlideAmount = nSamples / wt->GetRate();
if (!right)
desiredSlideAmount *= -1;
state.hSlideAmount = desiredSlideAmount;
TimeShiftHandle::DoSlideHorizontal( state, trackList, *track );
// 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 = t0 + state.hSlideAmount;
if (newT0 < state.capturedClip->GetStartTime())
newT0 = state.capturedClip->GetStartTime();
if (newT0 > state.capturedClip->GetEndTime())
newT0 = state.capturedClip->GetEndTime();
double diff = viewInfo.selectedRegion.duration();
viewInfo.selectedRegion.setTimes(newT0, newT0 + diff);
return state.hSlideAmount;
}
return 0.0;
}
void AudacityProject::DoClipLeftOrRight(bool right, bool keyUp )
{
if (keyUp) {
@ -3367,7 +3423,7 @@ void AudacityProject::DoClipLeftOrRight(bool right, bool keyUp )
auto &panel = *GetTrackPanel();
auto amount = TrackPanel::OnClipMove
auto amount = OnClipMove
( mViewInfo, panel.GetFocusedTrack(),
*GetTracks(), IsSyncLocked(), right );

View File

@ -164,6 +164,10 @@ void OnSelContractLeft(const wxEvent * evt);
void OnSelContractRight(const wxEvent * evt);
public:
static double OnClipMove
(ViewInfo &viewInfo, Track *track,
TrackList &trackList, bool syncLocked, bool right);
void DoClipLeftOrRight(bool right, bool keyUp );
void OnClipLeft(const wxEvent* evt);
void OnClipRight(const wxEvent* evt);

View File

@ -3303,364 +3303,6 @@ void TrackPanel::ForwardEventToEnvelope(wxMouseEvent & event)
}
}
// Helper for the above, adds a track's clips to mCapturedClipArray (eliminates
// duplication of this logic)
void TrackPanel::AddClipsToCaptured
( ClipMoveState &state, const ViewInfo &viewInfo,
Track *t, bool withinSelection )
{
if (withinSelection)
AddClipsToCaptured( state, t, viewInfo.selectedRegion.t0(),
viewInfo.selectedRegion.t1() );
else
AddClipsToCaptured( state, t, t->GetStartTime(), t->GetEndTime() );
}
// Adds a track's clips to state.capturedClipArray within a specified time
void TrackPanel::AddClipsToCaptured
( ClipMoveState &state, Track *t, double t0, double t1 )
{
if (t->GetKind() == Track::Wave)
{
for(const auto &clip: static_cast<WaveTrack*>(t)->GetClips())
{
if ( ! clip->AfterClip(t0) && ! clip->BeforeClip(t1) )
{
// Avoid getting clips that were already captured
bool newClip = true;
for (unsigned int i = 0; i < state.capturedClipArray.size(); ++i) {
if ( state.capturedClipArray[i].clip == clip.get() ) {
newClip = false;
break;
}
}
if (newClip)
state.capturedClipArray.push_back( TrackClip(t, clip.get()) );
}
}
}
else
{
// This handles label tracks rather heavy-handedly -- it would be nice to
// treat individual labels like clips
// Avoid adding a track twice
bool newClip = true;
for ( unsigned int i = 0; i < state.capturedClipArray.size(); ++i ) {
if ( state.capturedClipArray[i].track == t ) {
newClip = false;
break;
}
}
if (newClip) {
#ifdef USE_MIDI
// do not add NoteTrack if the data is outside of time bounds
if (t->GetKind() == Track::Note) {
if (t->GetEndTime() < t0 || t->GetStartTime() > t1)
return;
}
#endif
state.capturedClipArray.push_back(TrackClip(t, NULL));
}
}
}
namespace {
WaveClip *FindClipAtTime(WaveTrack *pTrack, double time)
{
if (pTrack) {
// WaveClip::GetClipAtX doesn't work unless the clip is on the screen and can return bad info otherwise
// instead calculate the time manually
double rate = pTrack->GetRate();
auto s0 = (sampleCount)(time * rate + 0.5);
if (s0 >= 0)
return pTrack->GetClipAtSample(s0);
}
return 0;
}
}
void TrackPanel::CreateListOfCapturedClips
( ClipMoveState &state, const ViewInfo &viewInfo, Track &capturedTrack,
TrackList &trackList, bool syncLocked, double clickTime )
{
// The captured clip is the focus, but we need to create a list
// of all clips that have to move, also...
state.capturedClipArray.clear();
// First, if click was in selection, capture selected clips; otherwise
// just the clicked-on clip
if ( state.capturedClipIsSelection ) {
TrackListIterator iter( &trackList );
for (Track *t = iter.First(); t; t = iter.Next()) {
if (t->GetSelected()) {
AddClipsToCaptured( state, viewInfo, t, true );
if (t->GetKind() != Track::Wave)
state.trackExclusions.push_back(t);
}
}
}
else {
state.capturedClipArray.push_back
(TrackClip( &capturedTrack, state.capturedClip ));
// Check for stereo partner
Track *partner = capturedTrack.GetLink();
WaveTrack *wt;
if (state.capturedClip &&
// Assume linked track is wave or null
nullptr != (wt = static_cast<WaveTrack*>(partner))) {
WaveClip *const clip = FindClipAtTime(wt, clickTime);
if (clip)
state.capturedClipArray.push_back(TrackClip(partner, clip));
}
}
// Now, if sync-lock is enabled, capture any clip that's linked to a
// captured clip.
if ( syncLocked ) {
// 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 < state.capturedClipArray.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 ( state.capturedClipArray[i].clip ) {
// Iterate over sync-lock group tracks.
SyncLockedTracksIterator git( &trackList );
for (Track *t = git.StartWith( state.capturedClipArray[i].track );
t; t = git.Next() )
{
AddClipsToCaptured(state, t,
state.capturedClipArray[i].clip->GetStartTime(),
state.capturedClipArray[i].clip->GetEndTime() );
if (t->GetKind() != Track::Wave)
state.trackExclusions.push_back(t);
}
}
#ifdef USE_MIDI
// Capture additional clips from NoteTracks
Track *nt = state.capturedClipArray[i].track;
if (nt->GetKind() == Track::Note) {
// Iterate over sync-lock group tracks.
SyncLockedTracksIterator git( &trackList );
for (Track *t = git.StartWith(nt); t; t = git.Next())
{
AddClipsToCaptured
( state, t, nt->GetStartTime(), nt->GetEndTime() );
if (t->GetKind() != Track::Wave)
state.trackExclusions.push_back(t);
}
}
#endif
}
}
}
#if 0
// Helper for the above, adds a track's clips to mCapturedClipArray (eliminates
// duplication of this logic)
void TrackPanel::AddClipsToCaptured
( ClipMoveState &state, const ViewInfo &viewInfo,
Track *t, bool withinSelection )
{
if (withinSelection)
AddClipsToCaptured( state, t, viewInfo.selectedRegion.t0(),
viewInfo.selectedRegion.t1() );
else
AddClipsToCaptured( state, t, t->GetStartTime(), t->GetEndTime() );
}
// Adds a track's clips to mCapturedClipArray within a specified time
void TrackPanel::AddClipsToCaptured
( ClipMoveState &state, Track *t, double t0, double t1 )
{
if (t->GetKind() == Track::Wave)
{
for(const auto &clip: static_cast<WaveTrack*>(t)->GetClips())
{
if ( ! clip->AfterClip(t0) && ! clip->BeforeClip(t1) )
{
// Avoid getting clips that were already captured
bool newClip = true;
for (unsigned int i = 0; i < state.capturedClipArray.size(); ++i) {
if ( state.capturedClipArray[i].clip == clip.get() ) {
newClip = false;
break;
}
}
if (newClip)
state.capturedClipArray.push_back( TrackClip(t, clip.get()) );
}
}
}
else
{
// This handles label tracks rather heavy-handedly -- it would be nice to
// treat individual labels like clips
// Avoid adding a track twice
bool newClip = true;
for ( unsigned int i = 0; i < state.capturedClipArray.size(); ++i ) {
if ( state.capturedClipArray[i].track == t ) {
newClip = false;
break;
}
}
if (newClip) {
#ifdef USE_MIDI
// do not add NoteTrack if the data is outside of time bounds
if (t->GetKind() == Track::Note) {
if (t->GetEndTime() < t0 || t->GetStartTime() > t1)
return;
}
#endif
state.capturedClipArray.push_back(TrackClip(t, NULL));
}
}
}
#endif
void TrackPanel::DoSlideHorizontal
( ClipMoveState &state, TrackList &trackList, Track &capturedTrack )
{
#ifdef USE_MIDI
if ( state.capturedClipArray.size() )
#else
if ( state.capturedClip )
#endif
{
double allowed;
double initialAllowed;
double safeBigDistance = 1000 + 2.0 * ( trackList.GetEndTime() -
trackList.GetStartTime() );
do { // loop to compute allowed, does not actually move anything yet
initialAllowed = state.hSlideAmount;
unsigned int i, j;
for ( i = 0; i < state.capturedClipArray.size(); ++i ) {
WaveTrack *track = (WaveTrack *)state.capturedClipArray[i].track;
WaveClip *clip = state. capturedClipArray[i].clip;
if (clip) { // only audio clips are used to compute allowed
// Move all other selected clips totally out of the way
// temporarily because they're all moving together and
// we want to find out if OTHER clips are in the way,
// not one of the moving ones
for ( j = 0; j < state.capturedClipArray.size(); j++ ) {
WaveClip *clip2 = state.capturedClipArray[j].clip;
if (clip2 && clip2 != clip)
clip2->Offset(-safeBigDistance);
}
if ( track->CanOffsetClip(clip, state.hSlideAmount, &allowed) ) {
if ( state.hSlideAmount != allowed ) {
state.hSlideAmount = allowed;
state.snapLeft = state.snapRight = -1; // see bug 1067
}
}
else {
state.hSlideAmount = 0.0;
state.snapLeft = state.snapRight = -1; // see bug 1067
}
for ( j = 0; j < state.capturedClipArray.size(); ++j ) {
WaveClip *clip2 = state.capturedClipArray[j].clip;
if (clip2 && clip2 != clip)
clip2->Offset(safeBigDistance);
}
}
}
} while ( state.hSlideAmount != initialAllowed );
if ( state.hSlideAmount != 0.0 ) { // finally, here is where clips are moved
unsigned int i;
for ( i = 0; i < state.capturedClipArray.size(); ++i ) {
Track *track = state.capturedClipArray[i].track;
WaveClip *clip = state.capturedClipArray[i].clip;
if (clip)
clip->Offset( state.hSlideAmount );
else
track->Offset( state.hSlideAmount );
}
}
}
else {
// For Shift key down, or
// For non wavetracks, specifically label tracks ...
capturedTrack.Offset( state.hSlideAmount );
Track* link = capturedTrack.GetLink();
if (link)
link->Offset( state.hSlideAmount );
}
}
// This function returns the amount moved. Possibly 0.0.
double TrackPanel::OnClipMove
( ViewInfo &viewInfo, Track *track,
TrackList &trackList, bool syncLocked, bool right )
{
// just dealing with clips in wave tracks for the moment. Note tracks??
if (track && track->GetKind() == Track::Wave) {
ClipMoveState state;
auto wt = static_cast<WaveTrack*>(track);
auto t0 = viewInfo.selectedRegion.t0();
state.capturedClip = wt->GetClipAtTime( t0 );
if (state.capturedClip == nullptr)
return 0.0;
state.capturedClipIsSelection =
track->GetSelected() && !viewInfo.selectedRegion.isPoint();
state.trackExclusions.clear();
CreateListOfCapturedClips
( state, viewInfo, *track, trackList, syncLocked, t0 );
auto desiredT0 = viewInfo.OffsetTimeByPixels( t0, ( right ? 1 : -1 ) );
auto desiredSlideAmount = desiredT0 - t0;
// set it to a sample point, and minimum of 1 sample point
if (!right)
desiredSlideAmount *= -1;
double nSamples = rint(wt->GetRate() * desiredSlideAmount);
nSamples = std::max(nSamples, 1.0);
desiredSlideAmount = nSamples / wt->GetRate();
if (!right)
desiredSlideAmount *= -1;
state.hSlideAmount = desiredSlideAmount;
DoSlideHorizontal( state, trackList, *track );
// 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 = t0 + state.hSlideAmount;
if (newT0 < state.capturedClip->GetStartTime())
newT0 = state.capturedClip->GetStartTime();
if (newT0 > state.capturedClip->GetEndTime())
newT0 = state.capturedClip->GetEndTime();
double diff = viewInfo.selectedRegion.duration();
viewInfo.selectedRegion.setTimes(newT0, newT0 + diff);
return state.hSlideAmount;
}
return 0.0;
}
/// Determines if drag zooming is active
bool TrackPanel::IsDragZooming(int zoomStart, int zoomEnd)
{

View File

@ -21,8 +21,6 @@
#include "SelectedRegion.h"
#include "WaveTrackLocation.h"
#include "Track.h"
#include "Snap.h"
#include "widgets/OverlayPanel.h"
#include "SelectionState.h"
@ -256,27 +254,6 @@ private:
const int DragThreshold = 3;// Anything over 3 pixels is a drag, else a click.
struct ClipMoveState {
// non-NULL only if click was in a WaveTrack and without Shift key:
WaveClip *capturedClip {};
bool capturedClipIsSelection {};
TrackArray trackExclusions {};
double hSlideAmount {};
TrackClipArray capturedClipArray {};
wxInt64 snapLeft { -1 }, snapRight { -1 };
void clear()
{
capturedClip = nullptr;
capturedClipIsSelection = false;
trackExclusions.clear();
hSlideAmount = 0;
capturedClipArray.clear();
snapLeft = snapRight = -1;
}
};
class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
public:
@ -371,10 +348,6 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
// (ignoring any fisheye)
virtual double GetScreenEndTime() const;
static double OnClipMove
(ViewInfo &viewInfo, Track *track,
TrackList &trackList, bool syncLocked, bool right);
protected:
virtual MixerBoard* GetMixerBoard();
/** @brief Populates the track pop-down menu with the common set of
@ -503,19 +476,6 @@ protected:
virtual void ForwardEventToWaveTrackEnvelope(wxMouseEvent & event);
virtual void ForwardEventToEnvelope(wxMouseEvent &event);
public:
static void DoSlideHorizontal
( ClipMoveState &state, TrackList &trackList, Track &capturedTrack );
static void CreateListOfCapturedClips
( ClipMoveState &state, const ViewInfo &viewInfo, Track &capturedTrack,
TrackList &trackList, bool syncLocked, double clickTime );
static void AddClipsToCaptured
( ClipMoveState &state, const ViewInfo &viewInfo,
Track *t, bool withinSelection );
static void AddClipsToCaptured
( ClipMoveState &state, Track *t, double t0, double t1 );
protected:
static bool IsDragZooming(int zoomStart, int zoomEnd);
virtual bool IsDragZooming() { return IsDragZooming(mZoomStart, mZoomEnd); }

View File

@ -89,7 +89,127 @@ TimeShiftHandle::~TimeShiftHandle()
{
}
namespace {
namespace
{
// Adds a track's clips to mCapturedClipArray within a specified time
void AddClipsToCaptured
(TrackClipArray &capturedClipArray, Track *pTrack, double t0, double t1)
{
if (pTrack->GetKind() == Track::Wave)
{
for(const auto &clip: static_cast<WaveTrack*>(pTrack)->GetClips())
{
if (!clip->AfterClip(t0) && !clip->BeforeClip(t1))
{
// Avoid getting clips that were already captured
bool newClip = true;
for (unsigned int ii = 0; newClip && ii < capturedClipArray.size(); ++ii)
newClip = (capturedClipArray[ii].clip != clip.get());
if (newClip)
capturedClipArray.push_back(TrackClip(pTrack, clip.get()));
}
}
}
else
{
// This handles label tracks rather heavy-handedly -- it would be nice to
// treat individual labels like clips
// Avoid adding a track twice
bool newClip = true;
for (unsigned int ii = 0; newClip && ii < capturedClipArray.size(); ++ii)
newClip = (capturedClipArray[ii].track != pTrack);
if (newClip) {
#ifdef USE_MIDI
// do not add NoteTrack if the data is outside of time bounds
if (pTrack->GetKind() == Track::Note) {
if (pTrack->GetEndTime() < t0 || pTrack->GetStartTime() > t1)
return;
}
#endif
capturedClipArray.push_back(TrackClip(pTrack, NULL));
}
}
}
// Helper for the above, adds a track's clips to mCapturedClipArray (eliminates
// duplication of this logic)
void AddClipsToCaptured
(TrackClipArray &capturedClipArray,
const ViewInfo &viewInfo, Track *pTrack, bool withinSelection)
{
if (withinSelection)
AddClipsToCaptured(capturedClipArray, pTrack,
viewInfo.selectedRegion.t0(), viewInfo.selectedRegion.t1());
else
AddClipsToCaptured(capturedClipArray, pTrack,
pTrack->GetStartTime(), pTrack->GetEndTime());
}
// Adds a track's clips to state.capturedClipArray within a specified time
void AddClipsToCaptured
( ClipMoveState &state, Track *t, double t0, double t1 )
{
if (t->GetKind() == Track::Wave)
{
for(const auto &clip: static_cast<WaveTrack*>(t)->GetClips())
{
if ( ! clip->AfterClip(t0) && ! clip->BeforeClip(t1) )
{
// Avoid getting clips that were already captured
bool newClip = true;
for (unsigned int i = 0; i < state.capturedClipArray.size(); ++i) {
if ( state.capturedClipArray[i].clip == clip.get() ) {
newClip = false;
break;
}
}
if (newClip)
state.capturedClipArray.push_back( TrackClip(t, clip.get()) );
}
}
}
else
{
// This handles label tracks rather heavy-handedly -- it would be nice to
// treat individual labels like clips
// Avoid adding a track twice
bool newClip = true;
for ( unsigned int i = 0; i < state.capturedClipArray.size(); ++i ) {
if ( state.capturedClipArray[i].track == t ) {
newClip = false;
break;
}
}
if (newClip) {
#ifdef USE_MIDI
// do not add NoteTrack if the data is outside of time bounds
if (t->GetKind() == Track::Note) {
if (t->GetEndTime() < t0 || t->GetStartTime() > t1)
return;
}
#endif
state.capturedClipArray.push_back(TrackClip(t, NULL));
}
}
}
// Helper for the above, adds a track's clips to mCapturedClipArray (eliminates
// duplication of this logic)
void AddClipsToCaptured
( ClipMoveState &state, const ViewInfo &viewInfo,
Track *t, bool withinSelection )
{
if (withinSelection)
AddClipsToCaptured( state, t, viewInfo.selectedRegion.t0(),
viewInfo.selectedRegion.t1() );
else
AddClipsToCaptured( state, t, t->GetStartTime(), t->GetEndTime() );
}
// Don't count right channels.
WaveTrack *NthAudioTrack(TrackList &list, int nn)
{
@ -117,6 +237,177 @@ namespace {
}
return -1;
}
WaveClip *FindClipAtTime(WaveTrack *pTrack, double time)
{
if (pTrack) {
// WaveClip::GetClipAtX doesn't work unless the clip is on the screen and can return bad info otherwise
// instead calculate the time manually
double rate = pTrack->GetRate();
auto s0 = (sampleCount)(time * rate + 0.5);
if (s0 >= 0)
return pTrack->GetClipAtSample(s0);
}
return 0;
}
}
void TimeShiftHandle::CreateListOfCapturedClips
( ClipMoveState &state, const ViewInfo &viewInfo, Track &capturedTrack,
TrackList &trackList, bool syncLocked, double clickTime )
{
// The captured clip is the focus, but we need to create a list
// of all clips that have to move, also...
state.capturedClipArray.clear();
// First, if click was in selection, capture selected clips; otherwise
// just the clicked-on clip
if ( state.capturedClipIsSelection ) {
TrackListIterator iter( &trackList );
for (Track *t = iter.First(); t; t = iter.Next()) {
if (t->GetSelected()) {
AddClipsToCaptured( state, viewInfo, t, true );
if (t->GetKind() != Track::Wave)
state.trackExclusions.push_back(t);
}
}
}
else {
state.capturedClipArray.push_back
(TrackClip( &capturedTrack, state.capturedClip ));
// Check for stereo partner
Track *partner = capturedTrack.GetLink();
WaveTrack *wt;
if (state.capturedClip &&
// Assume linked track is wave or null
nullptr != (wt = static_cast<WaveTrack*>(partner))) {
WaveClip *const clip = FindClipAtTime(wt, clickTime);
if (clip)
state.capturedClipArray.push_back(TrackClip(partner, clip));
}
}
// Now, if sync-lock is enabled, capture any clip that's linked to a
// captured clip.
if ( syncLocked ) {
// 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 < state.capturedClipArray.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 ( state.capturedClipArray[i].clip ) {
// Iterate over sync-lock group tracks.
SyncLockedTracksIterator git( &trackList );
for (Track *t = git.StartWith( state.capturedClipArray[i].track );
t; t = git.Next() )
{
AddClipsToCaptured(state, t,
state.capturedClipArray[i].clip->GetStartTime(),
state.capturedClipArray[i].clip->GetEndTime() );
if (t->GetKind() != Track::Wave)
state.trackExclusions.push_back(t);
}
}
#ifdef USE_MIDI
// Capture additional clips from NoteTracks
Track *nt = state.capturedClipArray[i].track;
if (nt->GetKind() == Track::Note) {
// Iterate over sync-lock group tracks.
SyncLockedTracksIterator git( &trackList );
for (Track *t = git.StartWith(nt); t; t = git.Next())
{
AddClipsToCaptured
( state, t, nt->GetStartTime(), nt->GetEndTime() );
if (t->GetKind() != Track::Wave)
state.trackExclusions.push_back(t);
}
}
#endif
}
}
}
void TimeShiftHandle::DoSlideHorizontal
( ClipMoveState &state, TrackList &trackList, Track &capturedTrack )
{
#ifdef USE_MIDI
if ( state.capturedClipArray.size() )
#else
if ( state.capturedClip )
#endif
{
double allowed;
double initialAllowed;
double safeBigDistance = 1000 + 2.0 * ( trackList.GetEndTime() -
trackList.GetStartTime() );
do { // loop to compute allowed, does not actually move anything yet
initialAllowed = state.hSlideAmount;
unsigned int i, j;
for ( i = 0; i < state.capturedClipArray.size(); ++i ) {
WaveTrack *track = (WaveTrack *)state.capturedClipArray[i].track;
WaveClip *clip = state. capturedClipArray[i].clip;
if (clip) { // only audio clips are used to compute allowed
// Move all other selected clips totally out of the way
// temporarily because they're all moving together and
// we want to find out if OTHER clips are in the way,
// not one of the moving ones
for ( j = 0; j < state.capturedClipArray.size(); j++ ) {
WaveClip *clip2 = state.capturedClipArray[j].clip;
if (clip2 && clip2 != clip)
clip2->Offset(-safeBigDistance);
}
if ( track->CanOffsetClip(clip, state.hSlideAmount, &allowed) ) {
if ( state.hSlideAmount != allowed ) {
state.hSlideAmount = allowed;
state.snapLeft = state.snapRight = -1; // see bug 1067
}
}
else {
state.hSlideAmount = 0.0;
state.snapLeft = state.snapRight = -1; // see bug 1067
}
for ( j = 0; j < state.capturedClipArray.size(); ++j ) {
WaveClip *clip2 = state.capturedClipArray[j].clip;
if (clip2 && clip2 != clip)
clip2->Offset(safeBigDistance);
}
}
}
} while ( state.hSlideAmount != initialAllowed );
if ( state.hSlideAmount != 0.0 ) { // finally, here is where clips are moved
unsigned int i;
for ( i = 0; i < state.capturedClipArray.size(); ++i ) {
Track *track = state.capturedClipArray[i].track;
WaveClip *clip = state.capturedClipArray[i].clip;
if (clip)
clip->Offset( state.hSlideAmount );
else
track->Offset( state.hSlideAmount );
}
}
}
else {
// For Shift key down, or
// For non wavetracks, specifically label tracks ...
capturedTrack.Offset( state.hSlideAmount );
Track* link = capturedTrack.GetLink();
if (link)
link->Offset( state.hSlideAmount );
}
}
UIHandle::Result TimeShiftHandle::Click
@ -169,7 +460,7 @@ UIHandle::Result TimeShiftHandle::Click
return Cancelled;
}
TrackPanel::CreateListOfCapturedClips
CreateListOfCapturedClips
( mClipMoveState, viewInfo, *pTrack, *trackList,
pProject->IsSyncLocked(), clickTime );
}
@ -459,7 +750,7 @@ UIHandle::Result TimeShiftHandle::Drag
mClipMoveState.hSlideAmount = desiredSlideAmount;
TrackPanel::DoSlideHorizontal( mClipMoveState, *trackList, *mCapturedTrack );
DoSlideHorizontal( mClipMoveState, *trackList, *mCapturedTrack );
if (mClipMoveState.capturedClipIsSelection) {
// Slide the selection, too

View File

@ -18,11 +18,30 @@ Paul Licameli
#include "../../Snap.h"
#include "../../Track.h"
#include "../../TrackPanel.h" // for ClipMoveState
struct HitTestResult;
class WaveClip;
struct ClipMoveState {
// non-NULL only if click was in a WaveTrack and without Shift key:
WaveClip *capturedClip {};
bool capturedClipIsSelection {};
TrackArray trackExclusions {};
double hSlideAmount {};
TrackClipArray capturedClipArray {};
wxInt64 snapLeft { -1 }, snapRight { -1 };
void clear()
{
capturedClip = nullptr;
capturedClipIsSelection = false;
trackExclusions.clear();
hSlideAmount = 0;
capturedClipArray.clear();
snapLeft = snapRight = -1;
}
};
class TimeShiftHandle final : public UIHandle
{
TimeShiftHandle();
@ -33,6 +52,15 @@ class TimeShiftHandle final : public UIHandle
(const AudacityProject *pProject, bool unsafe);
public:
// A utility function also used by menu commands
static void CreateListOfCapturedClips
( ClipMoveState &state, const ViewInfo &viewInfo, Track &capturedTrack,
TrackList &trackList, bool syncLocked, double clickTime );
// A utility function also used by menu commands
static void DoSlideHorizontal
( ClipMoveState &state, TrackList &trackList, Track &capturedTrack );
static HitTestResult HitAnywhere(const AudacityProject *pProject);
static HitTestResult HitTest
(const wxMouseEvent &event, const wxRect &rect, const AudacityProject *pProject);