mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-08 08:01:19 +02:00
Reorganize time-shifting code, no behavior change intended
This commit is contained in:
commit
aea04dcabf
@ -40,8 +40,10 @@ public:
|
||||
|
||||
Track *track;
|
||||
Track *origTrack;
|
||||
WaveTrack *dstTrack;
|
||||
WaveClip *clip;
|
||||
|
||||
// These fields are used only during time-shift dragging
|
||||
WaveTrack *dstTrack;
|
||||
std::shared_ptr<WaveClip> holder;
|
||||
};
|
||||
|
||||
|
@ -61,8 +61,6 @@ UIHandlePtr TimeShiftHandle::HitAnywhere
|
||||
(std::weak_ptr<TimeShiftHandle> &holder,
|
||||
const std::shared_ptr<Track> &pTrack, bool gripHit)
|
||||
{
|
||||
// After all that, it still may be unsafe to drag.
|
||||
// Even if so, make an informative cursor change from default to "banned."
|
||||
auto result = std::make_shared<TimeShiftHandle>( pTrack, gripHit );
|
||||
result = AssignUIHandlePtr(holder, result);
|
||||
return result;
|
||||
@ -99,84 +97,22 @@ TimeShiftHandle::~TimeShiftHandle()
|
||||
|
||||
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 )
|
||||
{
|
||||
auto &clips = state.capturedClipArray;
|
||||
|
||||
bool exclude = true;
|
||||
if (t->GetKind() == Track::Wave)
|
||||
{
|
||||
exclude = false;
|
||||
for(const auto &clip: static_cast<WaveTrack*>(t)->GetClips())
|
||||
{
|
||||
if ( ! clip->AfterClip(t0) && ! clip->BeforeClip(t1) )
|
||||
{
|
||||
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()) );
|
||||
}
|
||||
}
|
||||
! std::any_of( clips.begin(), clips.end(),
|
||||
[&](const TrackClip &c) { return c.clip == clip.get(); } ) )
|
||||
clips.emplace_back( t, clip.get() );
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -184,15 +120,8 @@ namespace
|
||||
// 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) {
|
||||
if( ! std::any_of( clips.begin(), clips.end(),
|
||||
[&](const TrackClip &c) { return c.track == t; } ) ) {
|
||||
#ifdef USE_MIDI
|
||||
// do not add NoteTrack if the data is outside of time bounds
|
||||
if (t->GetKind() == Track::Note) {
|
||||
@ -200,12 +129,14 @@ namespace
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
state.capturedClipArray.push_back(TrackClip(t, NULL));
|
||||
clips.emplace_back( t, nullptr );
|
||||
}
|
||||
}
|
||||
if ( exclude )
|
||||
state.trackExclusions.push_back(t);
|
||||
}
|
||||
|
||||
// Helper for the above, adds a track's clips to mCapturedClipArray (eliminates
|
||||
// Helper for the above, adds a track's clips to capturedClipArray (eliminates
|
||||
// duplication of this logic)
|
||||
void AddClipsToCaptured
|
||||
( ClipMoveState &state, const ViewInfo &viewInfo,
|
||||
@ -260,6 +191,31 @@ namespace
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DoOffset( ClipMoveState &state, Track *pTrack, double offset,
|
||||
WaveClip *pExcludedClip = nullptr )
|
||||
{
|
||||
auto &clips = state.capturedClipArray;
|
||||
if ( !clips.empty() ) {
|
||||
for (auto &clip : clips) {
|
||||
if (clip.clip) {
|
||||
if (clip.clip != pExcludedClip)
|
||||
clip.clip->Offset( offset );
|
||||
}
|
||||
else
|
||||
clip.track->Offset( offset );
|
||||
}
|
||||
}
|
||||
else if ( pTrack ) {
|
||||
// Was a shift-click
|
||||
for (auto channel =
|
||||
pTrack->GetLink() && !pTrack->GetLinked()
|
||||
? pTrack->GetLink() : pTrack;
|
||||
channel;
|
||||
channel = channel->GetLinked() ? channel->GetLink() : nullptr)
|
||||
channel->Offset( offset );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimeShiftHandle::CreateListOfCapturedClips
|
||||
@ -276,11 +232,8 @@ void TimeShiftHandle::CreateListOfCapturedClips
|
||||
if ( state.capturedClipIsSelection ) {
|
||||
TrackListIterator iter( &trackList );
|
||||
for (Track *t = iter.First(); t; t = iter.Next()) {
|
||||
if (t->GetSelected()) {
|
||||
if (t->GetSelected())
|
||||
AddClipsToCaptured( state, viewInfo, t, true );
|
||||
if (t->GetKind() != Track::Wave)
|
||||
state.trackExclusions.push_back(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -303,40 +256,33 @@ void TimeShiftHandle::CreateListOfCapturedClips
|
||||
// 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
|
||||
// AWD: capturedClipArray 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) {
|
||||
auto &trackClip = state.capturedClipArray[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 ) {
|
||||
if ( trackClip.clip ) {
|
||||
// Iterate over sync-lock group tracks.
|
||||
SyncLockedTracksIterator git( &trackList );
|
||||
for (Track *t = git.StartWith( state.capturedClipArray[i].track );
|
||||
for (Track *t = git.StartWith( trackClip.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);
|
||||
}
|
||||
trackClip.clip->GetStartTime(),
|
||||
trackClip.clip->GetEndTime() );
|
||||
}
|
||||
#ifdef USE_MIDI
|
||||
// Capture additional clips from NoteTracks
|
||||
Track *nt = state.capturedClipArray[i].track;
|
||||
Track *nt = trackClip.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
|
||||
}
|
||||
@ -346,11 +292,10 @@ void TimeShiftHandle::CreateListOfCapturedClips
|
||||
void TimeShiftHandle::DoSlideHorizontal
|
||||
( ClipMoveState &state, TrackList &trackList, Track &capturedTrack )
|
||||
{
|
||||
#ifdef USE_MIDI
|
||||
// Given a signed slide distance, move clips, but subject to constraint of
|
||||
// non-overlapping with other clips, so the distance may be adjusted toward
|
||||
// zero.
|
||||
if ( state.capturedClipArray.size() )
|
||||
#else
|
||||
if ( state.capturedClip )
|
||||
#endif
|
||||
{
|
||||
double allowed;
|
||||
double initialAllowed;
|
||||
@ -360,21 +305,18 @@ void TimeShiftHandle::DoSlideHorizontal
|
||||
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;
|
||||
for ( auto &trackClip : state.capturedClipArray ) {
|
||||
if (const auto clip = trackClip.clip) {
|
||||
// only audio clips are used to compute allowed
|
||||
const auto track = static_cast<WaveTrack *>( trackClip.track );
|
||||
|
||||
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);
|
||||
}
|
||||
DoOffset( state, nullptr, -safeBigDistance, clip );
|
||||
auto cleanup = finally( [&]
|
||||
{ DoOffset( state, nullptr, safeBigDistance, clip ); } );
|
||||
|
||||
if ( track->CanOffsetClip(clip, state.hSlideAmount, &allowed) ) {
|
||||
if ( state.hSlideAmount != allowed ) {
|
||||
@ -386,36 +328,18 @@ void TimeShiftHandle::DoSlideHorizontal
|
||||
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 );
|
||||
}
|
||||
}
|
||||
// finally, here is where clips are moved
|
||||
if ( state.hSlideAmount != 0.0 )
|
||||
DoOffset( state, nullptr, state.hSlideAmount );
|
||||
}
|
||||
else {
|
||||
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 );
|
||||
}
|
||||
DoOffset( state, &capturedTrack, state.hSlideAmount );
|
||||
}
|
||||
|
||||
UIHandle::Result TimeShiftHandle::Click
|
||||
@ -449,40 +373,41 @@ UIHandle::Result TimeShiftHandle::Click
|
||||
clickTime >= viewInfo.selectedRegion.t0() &&
|
||||
clickTime < viewInfo.selectedRegion.t1());
|
||||
|
||||
WaveTrack *wt = pTrack->GetKind() == Track::Wave
|
||||
? static_cast<WaveTrack*>(pTrack.get()) : nullptr;
|
||||
mClipMoveState.capturedClip = NULL;
|
||||
mClipMoveState.capturedClipArray.clear();
|
||||
|
||||
if ((wt
|
||||
#ifdef USE_MIDI
|
||||
|| pTrack->GetKind() == Track::Note
|
||||
#endif
|
||||
) && !event.ShiftDown())
|
||||
{
|
||||
#ifdef USE_MIDI
|
||||
if (!wt)
|
||||
mClipMoveState.capturedClip = nullptr;
|
||||
else
|
||||
#endif
|
||||
bool ok = true;
|
||||
bool captureClips = false;
|
||||
|
||||
if (!event.ShiftDown()) {
|
||||
WaveTrack *wt = pTrack->GetKind() == Track::Wave
|
||||
? static_cast<WaveTrack*>(pTrack.get()) : nullptr;
|
||||
if (wt)
|
||||
{
|
||||
mClipMoveState.capturedClip = wt->GetClipAtX(event.m_x);
|
||||
if (mClipMoveState.capturedClip == NULL)
|
||||
return Cancelled;
|
||||
if (nullptr ==
|
||||
(mClipMoveState.capturedClip = wt->GetClipAtX(event.m_x)))
|
||||
ok = false;
|
||||
else
|
||||
captureClips = true;
|
||||
}
|
||||
#ifdef USE_MIDI
|
||||
else if (pTrack->GetKind() == Track::Note)
|
||||
{
|
||||
captureClips = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( ! ok )
|
||||
return Cancelled;
|
||||
else if ( captureClips )
|
||||
CreateListOfCapturedClips
|
||||
( mClipMoveState, viewInfo, *pTrack, *trackList,
|
||||
pProject->IsSyncLocked(), clickTime );
|
||||
}
|
||||
else {
|
||||
// Shift was down, or track was not Wave or Note
|
||||
mClipMoveState.capturedClip = NULL;
|
||||
mClipMoveState.capturedClipArray.clear();
|
||||
}
|
||||
|
||||
mSlideUpDownOnly = event.CmdDown() && !multiToolModeActive;
|
||||
mRect = rect;
|
||||
mMouseClickX = event.m_x;
|
||||
const double selStart = viewInfo.PositionToTime(event.m_x, mRect.x);
|
||||
mClipMoveState.mMouseClickX = event.m_x;
|
||||
mSnapManager = std::make_shared<SnapManager>(trackList,
|
||||
&viewInfo,
|
||||
&mClipMoveState.capturedClipArray,
|
||||
@ -492,24 +417,263 @@ UIHandle::Result TimeShiftHandle::Click
|
||||
mClipMoveState.snapRight = -1;
|
||||
mSnapPreferRightEdge =
|
||||
mClipMoveState.capturedClip &&
|
||||
(fabs(selStart - mClipMoveState.capturedClip->GetEndTime()) <
|
||||
fabs(selStart - mClipMoveState.capturedClip->GetStartTime()));
|
||||
(fabs(clickTime - mClipMoveState.capturedClip->GetEndTime()) <
|
||||
fabs(clickTime - mClipMoveState.capturedClip->GetStartTime()));
|
||||
|
||||
return RefreshNone;
|
||||
}
|
||||
|
||||
namespace {
|
||||
double FindDesiredSlideAmount(
|
||||
const ViewInfo &viewInfo, wxCoord xx, const wxMouseEvent &event,
|
||||
SnapManager *pSnapManager,
|
||||
bool slideUpDownOnly, bool snapPreferRightEdge,
|
||||
ClipMoveState &state,
|
||||
Track &capturedTrack, Track &track )
|
||||
{
|
||||
if (slideUpDownOnly)
|
||||
return 0.0;
|
||||
else {
|
||||
double desiredSlideAmount =
|
||||
viewInfo.PositionToTime(event.m_x) -
|
||||
viewInfo.PositionToTime(state.mMouseClickX);
|
||||
double clipLeft = 0, clipRight = 0;
|
||||
|
||||
if (track.GetKind() == Track::Wave) {
|
||||
WaveTrack *const mtw = static_cast<WaveTrack*>(&track);
|
||||
const double rate = mtw->GetRate();
|
||||
// set it to a sample point
|
||||
desiredSlideAmount = rint(desiredSlideAmount * rate) / rate;
|
||||
}
|
||||
|
||||
// Adjust desiredSlideAmount using SnapManager
|
||||
if (pSnapManager) {
|
||||
if (state.capturedClip) {
|
||||
clipLeft = state.capturedClip->GetStartTime()
|
||||
+ desiredSlideAmount;
|
||||
clipRight = state.capturedClip->GetEndTime()
|
||||
+ desiredSlideAmount;
|
||||
}
|
||||
else {
|
||||
clipLeft = capturedTrack.GetStartTime() + desiredSlideAmount;
|
||||
clipRight = capturedTrack.GetEndTime() + desiredSlideAmount;
|
||||
}
|
||||
|
||||
auto results =
|
||||
pSnapManager->Snap(&capturedTrack, clipLeft, false);
|
||||
auto newClipLeft = results.outTime;
|
||||
results =
|
||||
pSnapManager->Snap(&capturedTrack, clipRight, false);
|
||||
auto newClipRight = results.outTime;
|
||||
|
||||
// Only one of them is allowed to snap
|
||||
if (newClipLeft != clipLeft && newClipRight != clipRight) {
|
||||
// Un-snap the un-preferred edge
|
||||
if (snapPreferRightEdge)
|
||||
newClipLeft = clipLeft;
|
||||
else
|
||||
newClipRight = clipRight;
|
||||
}
|
||||
|
||||
// Take whichever one snapped (if any) and compute the NEW desiredSlideAmount
|
||||
state.snapLeft = -1;
|
||||
state.snapRight = -1;
|
||||
if (newClipLeft != clipLeft) {
|
||||
const double difference = (newClipLeft - clipLeft);
|
||||
desiredSlideAmount += difference;
|
||||
state.snapLeft =
|
||||
viewInfo.TimeToPosition(newClipLeft, xx);
|
||||
}
|
||||
else if (newClipRight != clipRight) {
|
||||
const double difference = (newClipRight - clipRight);
|
||||
desiredSlideAmount += difference;
|
||||
state.snapRight =
|
||||
viewInfo.TimeToPosition(newClipRight, xx);
|
||||
}
|
||||
}
|
||||
return desiredSlideAmount;
|
||||
}
|
||||
}
|
||||
|
||||
bool FindCorrespondence(
|
||||
TrackList &trackList, Track &track, Track &capturedTrack,
|
||||
ClipMoveState &state)
|
||||
{
|
||||
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.
|
||||
Track *const pSrcTrack = trackClip.track;
|
||||
auto pDstTrack = NthAudioTrack(trackList,
|
||||
diff + TrackPosition(trackList, pSrcTrack));
|
||||
// Can only move mono to mono, or left to left, or right to right
|
||||
// And that must be so for each captured clip
|
||||
bool stereo = (pSrcTrack->GetLink() != 0);
|
||||
if (pDstTrack && stereo && !pSrcTrack->GetLinked())
|
||||
// Assume linked track is wave or null
|
||||
pDstTrack = static_cast<WaveTrack*>(pDstTrack->GetLink());
|
||||
bool ok = pDstTrack &&
|
||||
(stereo == (pDstTrack->GetLink() != 0)) &&
|
||||
(!stereo || (pSrcTrack->GetLinked() == pDstTrack->GetLinked()));
|
||||
if (ok)
|
||||
trackClip.dstTrack = pDstTrack;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckFit(
|
||||
const ViewInfo &viewInfo, wxCoord xx, ClipMoveState &state,
|
||||
double tolerance, double &desiredSlideAmount )
|
||||
{
|
||||
bool ok = true;
|
||||
double firstTolerance = tolerance;
|
||||
|
||||
// The desiredSlideAmount may change and the tolerance may get used up.
|
||||
for ( unsigned iPass = 0; iPass < 2 && ok; ++iPass ) {
|
||||
for ( auto &trackClip : state.capturedClipArray ) {
|
||||
WaveClip *const pSrcClip = trackClip.clip;
|
||||
if (pSrcClip) {
|
||||
ok = trackClip.dstTrack->CanInsertClip(
|
||||
pSrcClip, desiredSlideAmount, tolerance );
|
||||
if( !ok )
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If it fits ok, desiredSlideAmount could have been updated to get
|
||||
// the clip to fit.
|
||||
// Check again, in the new position, this time with zero tolerance.
|
||||
if (firstTolerance == 0)
|
||||
break;
|
||||
else
|
||||
tolerance = 0.0;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
struct TemporaryClipRemover {
|
||||
TemporaryClipRemover( ClipMoveState &clipMoveState )
|
||||
: state( clipMoveState )
|
||||
{
|
||||
// Pluck the moving clips out of their tracks
|
||||
for ( auto &trackClip : state.capturedClipArray ) {
|
||||
WaveClip *const pSrcClip = trackClip.clip;
|
||||
if (pSrcClip)
|
||||
trackClip.holder =
|
||||
// Assume track is wave because it has a clip
|
||||
static_cast<WaveTrack*>(trackClip.track)->
|
||||
RemoveAndReturnClip(pSrcClip);
|
||||
}
|
||||
}
|
||||
|
||||
void Fail()
|
||||
{
|
||||
// Cause destructor to put all clips back where they came from
|
||||
for ( auto &trackClip : state.capturedClipArray )
|
||||
trackClip.dstTrack = static_cast<WaveTrack*>(trackClip.track);
|
||||
}
|
||||
|
||||
~TemporaryClipRemover()
|
||||
{
|
||||
// Complete (or roll back) the vertical move.
|
||||
// Put moving clips into their destination tracks
|
||||
// which become the source tracks when we move again
|
||||
for ( auto &trackClip : state.capturedClipArray ) {
|
||||
WaveClip *const pSrcClip = trackClip.clip;
|
||||
if (pSrcClip) {
|
||||
const auto dstTrack = trackClip.dstTrack;
|
||||
dstTrack->AddClip(std::move(trackClip.holder));
|
||||
trackClip.track = dstTrack;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClipMoveState &state;
|
||||
};
|
||||
}
|
||||
|
||||
bool TimeShiftHandle::DoSlideVertical
|
||||
( ViewInfo &viewInfo, wxCoord xx,
|
||||
ClipMoveState &state, TrackList &trackList, Track &capturedTrack,
|
||||
Track &dstTrack, double &desiredSlideAmount )
|
||||
{
|
||||
if (!FindCorrespondence( trackList, dstTrack, capturedTrack, state))
|
||||
return false;
|
||||
|
||||
// Having passed that test, remove clips temporarily from their
|
||||
// tracks, so moving clips don't interfere with each other
|
||||
// when we call CanInsertClip()
|
||||
TemporaryClipRemover remover{ state };
|
||||
|
||||
// Now check that the move is possible
|
||||
double slide = desiredSlideAmount; // remember amount requested.
|
||||
// The test for tolerance will need review with FishEye!
|
||||
// The tolerance is supposed to be the time for one pixel,
|
||||
// i.e. one pixel tolerance at current zoom.
|
||||
double tolerance =
|
||||
viewInfo.PositionToTime(xx + 1) - viewInfo.PositionToTime(xx);
|
||||
bool ok = CheckFit( viewInfo, xx, state, tolerance, desiredSlideAmount );
|
||||
|
||||
if (!ok) {
|
||||
// Failure, even with using tolerance.
|
||||
remover.Fail();
|
||||
|
||||
// Failure -- we'll put clips back where they were
|
||||
// ok will next indicate if a horizontal slide is OK.
|
||||
tolerance = 0.0;
|
||||
desiredSlideAmount = slide;
|
||||
ok = CheckFit( viewInfo, xx, state, tolerance, desiredSlideAmount );
|
||||
for ( auto &trackClip : state.capturedClipArray) {
|
||||
WaveClip *const pSrcClip = trackClip.clip;
|
||||
if (pSrcClip){
|
||||
|
||||
// Attempt to move to a new track did not work.
|
||||
// Put the clip back appropriately shifted!
|
||||
if( ok)
|
||||
trackClip.holder->Offset(slide);
|
||||
}
|
||||
}
|
||||
// Make the offset permanent; start from a "clean slate"
|
||||
if( ok ) {
|
||||
state.mMouseClickX = xx;
|
||||
if (state.capturedClipIsSelection) {
|
||||
// Slide the selection, too
|
||||
viewInfo.selectedRegion.move( slide );
|
||||
}
|
||||
state.hSlideAmount = 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make the offset permanent; start from a "clean slate"
|
||||
state.mMouseClickX = xx;
|
||||
return true;
|
||||
}
|
||||
|
||||
UIHandle::Result TimeShiftHandle::Drag
|
||||
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
|
||||
{
|
||||
using namespace RefreshCode;
|
||||
const bool unsafe = pProject->IsAudioActive();
|
||||
if (unsafe) {
|
||||
this->Cancel(pProject);
|
||||
return RefreshAll | Cancelled;
|
||||
}
|
||||
|
||||
const wxMouseEvent &event = evt.event;
|
||||
ViewInfo &viewInfo = pProject->GetViewInfo();
|
||||
|
||||
// We may switch pTrack to its stereo partner below
|
||||
Track *track = dynamic_cast<Track*>(evt.pCell.get());
|
||||
|
||||
// Uncommenting this permits drag to continue to work even over the controls area
|
||||
/*
|
||||
pTrack = static_cast<CommonTrackPanelCell*>(evt.pCell)->FindTrack().get();
|
||||
track = static_cast<CommonTrackPanelCell*>(evt.pCell)->FindTrack().get();
|
||||
*/
|
||||
|
||||
if (!track) {
|
||||
@ -526,13 +690,6 @@ UIHandle::Result TimeShiftHandle::Drag
|
||||
return RefreshCode::RefreshNone;
|
||||
|
||||
|
||||
using namespace RefreshCode;
|
||||
const bool unsafe = pProject->IsAudioActive();
|
||||
if (unsafe) {
|
||||
this->Cancel(pProject);
|
||||
return RefreshAll | Cancelled;
|
||||
}
|
||||
|
||||
TrackList *const trackList = pProject->GetTracks();
|
||||
|
||||
// GM: DoSlide now implementing snap-to
|
||||
@ -541,28 +698,8 @@ UIHandle::Result TimeShiftHandle::Drag
|
||||
// Start by undoing the current slide amount; everything
|
||||
// happens relative to the original horizontal position of
|
||||
// each clip...
|
||||
#ifdef USE_MIDI
|
||||
if (mClipMoveState.capturedClipArray.size())
|
||||
#else
|
||||
if (mClipMoveState.capturedClip)
|
||||
#endif
|
||||
{
|
||||
for (unsigned ii = 0; ii < mClipMoveState.capturedClipArray.size(); ++ii) {
|
||||
if (mClipMoveState.capturedClipArray[ii].clip)
|
||||
mClipMoveState.capturedClipArray[ii].clip->Offset
|
||||
( -mClipMoveState.hSlideAmount );
|
||||
else
|
||||
mClipMoveState.capturedClipArray[ii].track->Offset
|
||||
( -mClipMoveState.hSlideAmount );
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Was a shift-click
|
||||
mCapturedTrack->Offset( -mClipMoveState.hSlideAmount );
|
||||
Track *const link = mCapturedTrack->GetLink();
|
||||
if (link)
|
||||
link->Offset( -mClipMoveState.hSlideAmount );
|
||||
}
|
||||
DoOffset(
|
||||
mClipMoveState, mCapturedTrack.get(), -mClipMoveState.hSlideAmount );
|
||||
|
||||
if ( mClipMoveState.capturedClipIsSelection ) {
|
||||
// Slide the selection, too
|
||||
@ -570,69 +707,10 @@ UIHandle::Result TimeShiftHandle::Drag
|
||||
}
|
||||
mClipMoveState.hSlideAmount = 0.0;
|
||||
|
||||
double desiredSlideAmount;
|
||||
if (mSlideUpDownOnly) {
|
||||
desiredSlideAmount = 0.0;
|
||||
}
|
||||
else {
|
||||
desiredSlideAmount =
|
||||
viewInfo.PositionToTime(event.m_x) -
|
||||
viewInfo.PositionToTime(mMouseClickX);
|
||||
double clipLeft = 0, clipRight = 0;
|
||||
|
||||
if (pTrack->GetKind() == Track::Wave) {
|
||||
WaveTrack *const mtw = static_cast<WaveTrack*>(pTrack.get());
|
||||
const double rate = mtw->GetRate();
|
||||
// set it to a sample point
|
||||
desiredSlideAmount = rint(desiredSlideAmount * rate) / rate;
|
||||
}
|
||||
|
||||
// Adjust desiredSlideAmount using SnapManager
|
||||
if (mSnapManager.get()) {
|
||||
if (mClipMoveState.capturedClip) {
|
||||
clipLeft = mClipMoveState.capturedClip->GetStartTime()
|
||||
+ desiredSlideAmount;
|
||||
clipRight = mClipMoveState.capturedClip->GetEndTime()
|
||||
+ desiredSlideAmount;
|
||||
}
|
||||
else {
|
||||
clipLeft = mCapturedTrack->GetStartTime() + desiredSlideAmount;
|
||||
clipRight = mCapturedTrack->GetEndTime() + desiredSlideAmount;
|
||||
}
|
||||
|
||||
auto results =
|
||||
mSnapManager->Snap(mCapturedTrack.get(), clipLeft, false);
|
||||
auto newClipLeft = results.outTime;
|
||||
results =
|
||||
mSnapManager->Snap(mCapturedTrack.get(), clipRight, false);
|
||||
auto newClipRight = results.outTime;
|
||||
|
||||
// Only one of them is allowed to snap
|
||||
if (newClipLeft != clipLeft && newClipRight != clipRight) {
|
||||
// Un-snap the un-preferred edge
|
||||
if (mSnapPreferRightEdge)
|
||||
newClipLeft = clipLeft;
|
||||
else
|
||||
newClipRight = clipRight;
|
||||
}
|
||||
|
||||
// Take whichever one snapped (if any) and compute the NEW desiredSlideAmount
|
||||
mClipMoveState.snapLeft = -1;
|
||||
mClipMoveState.snapRight = -1;
|
||||
if (newClipLeft != clipLeft) {
|
||||
const double difference = (newClipLeft - clipLeft);
|
||||
desiredSlideAmount += difference;
|
||||
mClipMoveState.snapLeft =
|
||||
viewInfo.TimeToPosition(newClipLeft, mRect.x);
|
||||
}
|
||||
else if (newClipRight != clipRight) {
|
||||
const double difference = (newClipRight - clipRight);
|
||||
desiredSlideAmount += difference;
|
||||
mClipMoveState.snapRight =
|
||||
viewInfo.TimeToPosition(newClipRight, mRect.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
double desiredSlideAmount =
|
||||
FindDesiredSlideAmount( viewInfo, mRect.x, event, mSnapManager.get(),
|
||||
mSlideUpDownOnly, mSnapPreferRightEdge, mClipMoveState,
|
||||
*mCapturedTrack, *pTrack );
|
||||
|
||||
// Scroll during vertical drag.
|
||||
// EnsureVisible(pTrack); //vvv Gale says this has problems on Linux, per bug 393 thread. Revert for 2.0.2.
|
||||
@ -645,140 +723,13 @@ UIHandle::Result TimeShiftHandle::Drag
|
||||
pTrack->GetKind() == Track::Wave
|
||||
/* && !mCapturedClipIsSelection*/)
|
||||
{
|
||||
const int diff =
|
||||
TrackPosition(*trackList, pTrack.get()) -
|
||||
TrackPosition(*trackList, mCapturedTrack.get());
|
||||
for ( unsigned ii = 0, nn = mClipMoveState.capturedClipArray.size();
|
||||
ii < nn; ++ii ) {
|
||||
TrackClip &trackClip = mClipMoveState.capturedClipArray[ii];
|
||||
if (trackClip.clip) {
|
||||
// Move all clips up or down by an equal count of audio tracks.
|
||||
Track *const pSrcTrack = trackClip.track;
|
||||
auto pDstTrack = NthAudioTrack(*trackList,
|
||||
diff + TrackPosition(*trackList, pSrcTrack));
|
||||
// Can only move mono to mono, or left to left, or right to right
|
||||
// And that must be so for each captured clip
|
||||
bool stereo = (pSrcTrack->GetLink() != 0);
|
||||
if (pDstTrack && stereo && !pSrcTrack->GetLinked())
|
||||
// Assume linked track is wave or null
|
||||
pDstTrack = static_cast<WaveTrack*>(pDstTrack->GetLink());
|
||||
bool ok = pDstTrack &&
|
||||
(stereo == (pDstTrack->GetLink() != 0)) &&
|
||||
(!stereo || (pSrcTrack->GetLinked() == pDstTrack->GetLinked()));
|
||||
if (ok)
|
||||
trackClip.dstTrack = pDstTrack;
|
||||
else
|
||||
return RefreshAll;
|
||||
}
|
||||
}
|
||||
|
||||
// Having passed that test, remove clips temporarily from their
|
||||
// tracks, so moving clips don't interfere with each other
|
||||
// when we call CanInsertClip()
|
||||
for ( unsigned ii = 0, nn = mClipMoveState.capturedClipArray.size();
|
||||
ii < nn; ++ii ) {
|
||||
TrackClip &trackClip = mClipMoveState.capturedClipArray[ii];
|
||||
WaveClip *const pSrcClip = trackClip.clip;
|
||||
if (pSrcClip)
|
||||
trackClip.holder =
|
||||
// Assume track is wave because it has a clip
|
||||
static_cast<WaveTrack*>(trackClip.track)->
|
||||
RemoveAndReturnClip(pSrcClip);
|
||||
}
|
||||
|
||||
// Now check that the move is possible
|
||||
bool ok = true;
|
||||
// The test for tolerance will need review with FishEye!
|
||||
// The tolerance is supposed to be the time for one pixel, i.e. one pixel tolerance
|
||||
// at current zoom.
|
||||
double slide = desiredSlideAmount; // remember amount requested.
|
||||
double tolerance = viewInfo.PositionToTime(event.m_x+1) - viewInfo.PositionToTime(event.m_x);
|
||||
|
||||
// The desiredSlideAmount may change and the tolerance may get used up.
|
||||
for ( unsigned ii = 0, nn = mClipMoveState.capturedClipArray.size();
|
||||
ok && ii < nn; ++ii) {
|
||||
TrackClip &trackClip = mClipMoveState.capturedClipArray[ii];
|
||||
WaveClip *const pSrcClip = trackClip.clip;
|
||||
if (pSrcClip)
|
||||
ok = trackClip.dstTrack->CanInsertClip(pSrcClip, desiredSlideAmount, tolerance);
|
||||
}
|
||||
|
||||
if( ok ) {
|
||||
// fits ok, but desiredSlideAmount could have been updated to get the clip to fit.
|
||||
// Check again, in the new position, this time with zero tolerance.
|
||||
tolerance = 0.0;
|
||||
for ( unsigned ii = 0, nn = mClipMoveState.capturedClipArray.size();
|
||||
ok && ii < nn; ++ii) {
|
||||
TrackClip &trackClip = mClipMoveState.capturedClipArray[ii];
|
||||
WaveClip *const pSrcClip = trackClip.clip;
|
||||
if (pSrcClip)
|
||||
ok = trackClip.dstTrack->CanInsertClip(pSrcClip, desiredSlideAmount, tolerance);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
// Failure, even with using tolerance.
|
||||
// Failure -- we'll put clips back where they were
|
||||
// ok will next indicate if a horizontal slide is OK.
|
||||
ok = true; // assume slide is OK.
|
||||
tolerance = 0.0;
|
||||
desiredSlideAmount = slide;
|
||||
for ( unsigned ii = 0, nn = mClipMoveState.capturedClipArray.size();
|
||||
ii < nn; ++ii) {
|
||||
TrackClip &trackClip = mClipMoveState.capturedClipArray[ii];
|
||||
WaveClip *const pSrcClip = trackClip.clip;
|
||||
if (pSrcClip){
|
||||
// back to the track it came from...
|
||||
trackClip.dstTrack = static_cast<WaveTrack*>(trackClip.track);
|
||||
ok = ok && trackClip.dstTrack->CanInsertClip(pSrcClip, desiredSlideAmount, tolerance);
|
||||
}
|
||||
}
|
||||
for ( unsigned ii = 0, nn = mClipMoveState.capturedClipArray.size();
|
||||
ii < nn; ++ii) {
|
||||
TrackClip &trackClip = mClipMoveState.capturedClipArray[ii];
|
||||
WaveClip *const pSrcClip = trackClip.clip;
|
||||
if (pSrcClip){
|
||||
|
||||
// Attempt to move to a new track did not work.
|
||||
// Put the clip back appropriately shifted!
|
||||
if( ok)
|
||||
trackClip.holder->Offset(slide);
|
||||
// Assume track is wave because it has a clip
|
||||
static_cast<WaveTrack*>(trackClip.track)->
|
||||
AddClip(std::move(trackClip.holder));
|
||||
}
|
||||
}
|
||||
// Make the offset permanent; start from a "clean slate"
|
||||
if( ok ) {
|
||||
mMouseClickX = event.m_x;
|
||||
if (mClipMoveState.capturedClipIsSelection) {
|
||||
// Slide the selection, too
|
||||
viewInfo.selectedRegion.move( slide );
|
||||
}
|
||||
mClipMoveState.hSlideAmount = 0;
|
||||
}
|
||||
|
||||
return RefreshAll;
|
||||
}
|
||||
else {
|
||||
// Do the vertical moves of clips
|
||||
for ( unsigned ii = 0, nn = mClipMoveState.capturedClipArray.size();
|
||||
ii < nn; ++ii ) {
|
||||
TrackClip &trackClip = mClipMoveState.capturedClipArray[ii];
|
||||
WaveClip *const pSrcClip = trackClip.clip;
|
||||
if (pSrcClip) {
|
||||
const auto dstTrack = trackClip.dstTrack;
|
||||
dstTrack->AddClip(std::move(trackClip.holder));
|
||||
trackClip.track = dstTrack;
|
||||
}
|
||||
}
|
||||
|
||||
if ( DoSlideVertical( viewInfo, event.m_x, mClipMoveState,
|
||||
*trackList, *mCapturedTrack, *pTrack, desiredSlideAmount ) ) {
|
||||
mCapturedTrack = pTrack;
|
||||
mDidSlideVertically = true;
|
||||
|
||||
// Make the offset permanent; start from a "clean slate"
|
||||
mMouseClickX = event.m_x;
|
||||
}
|
||||
else
|
||||
return RefreshAll;
|
||||
|
||||
// Not done yet, check for horizontal movement.
|
||||
slidVertically = true;
|
||||
@ -833,19 +784,18 @@ UIHandle::Result TimeShiftHandle::Release
|
||||
if ( !mDidSlideVertically && mClipMoveState.hSlideAmount == 0 )
|
||||
return result;
|
||||
|
||||
for ( size_t ii = 0; ii < mClipMoveState.capturedClipArray.size(); ++ii )
|
||||
for ( auto &trackClip : mClipMoveState.capturedClipArray )
|
||||
{
|
||||
TrackClip &trackClip = mClipMoveState.capturedClipArray[ii];
|
||||
WaveClip* pWaveClip = trackClip.clip;
|
||||
// Note that per AddClipsToCaptured(Track *t, double t0, double t1),
|
||||
// in the non-WaveTrack case, the code adds a NULL clip to mCapturedClipArray,
|
||||
// in the non-WaveTrack case, the code adds a NULL clip to capturedClipArray,
|
||||
// so we have to check for that any time we're going to deref it.
|
||||
// Previous code did not check it here, and that caused bug 367 crash.
|
||||
if (pWaveClip &&
|
||||
trackClip.track != trackClip.origTrack)
|
||||
{
|
||||
// Now that user has dropped the clip into a different track,
|
||||
// make sure the sample rate matches the destination track (mCapturedTrack).
|
||||
// make sure the sample rate matches the destination track.
|
||||
// Assume the clip was dropped in a wave track
|
||||
pWaveClip->Resample
|
||||
(static_cast<WaveTrack*>(trackClip.track)->GetRate());
|
||||
|
@ -30,6 +30,8 @@ struct ClipMoveState {
|
||||
TrackClipArray capturedClipArray {};
|
||||
wxInt64 snapLeft { -1 }, snapRight { -1 };
|
||||
|
||||
int mMouseClickX{};
|
||||
|
||||
void clear()
|
||||
{
|
||||
capturedClip = nullptr;
|
||||
@ -38,6 +40,7 @@ struct ClipMoveState {
|
||||
hSlideAmount = 0;
|
||||
capturedClipArray.clear();
|
||||
snapLeft = snapRight = -1;
|
||||
mMouseClickX = 0;
|
||||
}
|
||||
};
|
||||
|
||||
@ -65,6 +68,14 @@ public:
|
||||
static void DoSlideHorizontal
|
||||
( ClipMoveState &state, TrackList &trackList, Track &capturedTrack );
|
||||
|
||||
// Try to move clips from one WaveTrack to another, before also moving
|
||||
// by some horizontal amount, which may be slightly adjusted to fit the
|
||||
// destination tracks.
|
||||
static bool DoSlideVertical
|
||||
( ViewInfo &viewInfo, wxCoord xx,
|
||||
ClipMoveState &state, TrackList &trackList, Track &capturedTrack,
|
||||
Track &dstTrack, double &desiredSlideAmount );
|
||||
|
||||
static UIHandlePtr HitAnywhere
|
||||
(std::weak_ptr<TimeShiftHandle> &holder,
|
||||
const std::shared_ptr<Track> &pTrack, bool gripHit);
|
||||
@ -108,8 +119,6 @@ private:
|
||||
|
||||
bool mSnapPreferRightEdge{};
|
||||
|
||||
int mMouseClickX{};
|
||||
|
||||
// Handles snapping the selection boundaries or track boundaries to
|
||||
// line up with existing tracks or labels. mSnapLeft and mSnapRight
|
||||
// are the horizontal index of pixels to display user feedback
|
||||
|
Loading…
x
Reference in New Issue
Block a user