1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-04-30 15:49:41 +02:00

TrackShifter handles removal and reinsertion of clips

This commit is contained in:
Paul Licameli 2020-09-16 02:30:32 -04:00
parent 633b2e28bc
commit 716008e293
3 changed files with 106 additions and 31 deletions

View File

@ -10,6 +10,8 @@ Paul Licameli split from TrackPanel.cpp
#include "WaveTrackView.h"
#include <unordered_set>
#include "CutlineHandle.h"
#include "../../../../Experimental.h"
@ -1363,8 +1365,48 @@ public:
return desiredOffset;
}
Intervals Detach() override
{
for ( auto &interval: mMoving ) {
auto pData = static_cast<WaveTrack::IntervalData*>( interval.Extra() );
auto pClip = pData->GetClip().get();
// interval will still hold the clip, so ignore the return:
(void) mpTrack->RemoveAndReturnClip(pClip);
mMigrated.erase(pClip);
}
return std::move( mMoving );
}
bool Attach( Intervals intervals ) override
{
for (auto &interval : intervals) {
auto pData = static_cast<WaveTrack::IntervalData*>( interval.Extra() );
auto pClip = pData->GetClip();
if ( !mpTrack->AddClip( pClip ) )
return false;
mMigrated.insert( pClip.get() );
mMoving.emplace_back( std::move( interval ) );
}
return true;
}
bool FinishMigration() override
{
auto rate = mpTrack->GetRate();
for (auto pClip : mMigrated) {
// Now that user has dropped the clip into a different track,
// make sure the sample rate matches the destination track.
pClip->Resample(rate);
pClip->MarkChanged();
}
return true;
}
private:
std::shared_ptr<WaveTrack> mpTrack;
// Clips that may require resampling
std::unordered_set<WaveClip *> mMigrated;
};
using MakeWaveTrackShifter = MakeTrackShifter::Override<WaveTrack>;

View File

@ -275,6 +275,21 @@ bool TrackShifter::CommonMayMigrateTo(Track &otherTrack)
return false;
}
auto TrackShifter::Detach() -> Intervals
{
return {};
}
bool TrackShifter::Attach( Intervals )
{
return true;
}
bool TrackShifter::FinishMigration()
{
return true;
}
void TrackShifter::InitIntervals()
{
mMoving.clear();
@ -811,19 +826,20 @@ namespace {
return ok;
}
[[noreturn]] void MigrationFailure() {
// Tracks may be in an inconsistent state; throw to the application
// handler which restores consistency from undo history
throw SimpleMessageBoxException{
XO("Could not shift between tracks")};
}
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);
}
for (auto &pair : state.shifters)
detached[pair.first] = pair.second->Detach();
}
void Fail()
@ -831,9 +847,11 @@ namespace {
// Cause destructor to put all clips back where they came from
for ( auto &trackClip : state.capturedClipArray )
trackClip.dstTrack = static_cast<WaveTrack*>(trackClip.track);
failed = true;
}
~TemporaryClipRemover()
void Reinsert(
std::unordered_map< Track*, Track* > &correspondence )
{
// Complete (or roll back) the vertical move.
// Put moving clips into their destination tracks
@ -842,14 +860,23 @@ namespace {
WaveClip *const pSrcClip = trackClip.clip;
if (pSrcClip) {
const auto dstTrack = trackClip.dstTrack;
// To do: check and propagate the error! Can't from a dtor
(void) dstTrack->AddClip(trackClip.holder);
trackClip.track = dstTrack;
}
}
for (auto &pair : detached) {
auto pTrack = pair.first;
if (!failed && correspondence.count(pTrack))
pTrack = correspondence[pTrack];
auto &pShifter = state.shifters[pTrack];
if (!pShifter->Attach( std::move( pair.second ) ))
MigrationFailure();
}
}
ClipMoveState &state;
std::unordered_map<Track*, TrackShifter::Intervals> detached;
bool failed = false;
};
}
@ -879,11 +906,13 @@ bool TimeShiftHandle::DoSlideVertical
if (!ok) {
// Failure, even with using tolerance.
remover.Fail();
remover.Reinsert( correspondence );
return false;
}
// Make the offset permanent; start from a "clean slate"
state.mMouseClickX = xx;
remover.Reinsert( correspondence );
return true;
}
@ -1012,26 +1041,11 @@ UIHandle::Result TimeShiftHandle::Release
if ( !mDidSlideVertically && mClipMoveState.hSlideAmount == 0 )
return result;
for ( auto &trackClip : mClipMoveState.capturedClipArray )
{
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 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.
// Assume the clip was dropped in a wave track
pWaveClip->Resample
(static_cast<WaveTrack*>(trackClip.track)->GetRate());
pWaveClip->MarkChanged();
}
}
for ( auto &pair : mClipMoveState.shifters )
if ( !pair.second->FinishMigration() )
MigrationFailure();
TranslatableString msg;
bool consolidate;
if (mDidSlideVertically) {

View File

@ -79,6 +79,26 @@ public:
/*! Default implementation returns false */
virtual bool MayMigrateTo( Track &otherTrack );
//! Remove all moving intervals from the track, if possible
/*! Default implementation does nothing */
virtual Intervals Detach();
//! Put moving intervals into the track, which may have migrated from another
/*! @return success
In case of failure, track states are unspecified
Default implementation does nothing and returns true */
virtual bool Attach( Intervals intervals );
//! When dragging is done, do (once) the final steps of migration (which may be expensive)
/*! @return success
In case of failure, track states are unspecified
Default implementation does nothing and returns true */
virtual bool FinishMigration();
protected:
/*! Unfix any of the intervals that intersect the given one; may be useful to override `SelectInterval()` */
void CommonSelectInterval( const TrackInterval &interval );
@ -133,7 +153,6 @@ public:
// These fields are used only during time-shift dragging
WaveTrack *dstTrack;
std::shared_ptr<WaveClip> holder;
};
using TrackClipArray = std::vector <TrackClip>;