mirror of
https://github.com/cookiengineer/audacity
synced 2025-04-30 15:49:41 +02:00
Last virtuals, for horizontal shift; no more use of Track subclasses
This commit is contained in:
commit
69d5c1b12e
@ -2212,32 +2212,43 @@ int WaveTrack::GetNumClips() const
|
|||||||
return mClips.size();
|
return mClips.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WaveTrack::CanOffsetClip(WaveClip* clip, double amount,
|
bool WaveTrack::CanOffsetClips(
|
||||||
double *allowedAmount /* = NULL */)
|
const std::vector<WaveClip*> &clips,
|
||||||
|
double amount,
|
||||||
|
double *allowedAmount /* = NULL */)
|
||||||
{
|
{
|
||||||
if (allowedAmount)
|
if (allowedAmount)
|
||||||
*allowedAmount = amount;
|
*allowedAmount = amount;
|
||||||
|
|
||||||
for (const auto &c: mClips)
|
const auto &moving = [&](WaveClip *clip){
|
||||||
{
|
// linear search might be improved, but expecting few moving clips
|
||||||
if (c.get() != clip && c->GetStartTime() < clip->GetEndTime()+amount &&
|
// compared with the fixed clips
|
||||||
c->GetEndTime() > clip->GetStartTime()+amount)
|
return clips.end() != std::find( clips.begin(), clips.end(), clip );
|
||||||
{
|
};
|
||||||
if (!allowedAmount)
|
|
||||||
return false; // clips overlap
|
|
||||||
|
|
||||||
if (amount > 0)
|
for (const auto &c: mClips) {
|
||||||
|
if ( moving( c.get() ) )
|
||||||
|
continue;
|
||||||
|
for (const auto clip : clips) {
|
||||||
|
if (c->GetStartTime() < clip->GetEndTime() + amount &&
|
||||||
|
c->GetEndTime() > clip->GetStartTime() + amount)
|
||||||
{
|
{
|
||||||
if (c->GetStartTime()-clip->GetEndTime() < *allowedAmount)
|
if (!allowedAmount)
|
||||||
*allowedAmount = c->GetStartTime()-clip->GetEndTime();
|
return false; // clips overlap
|
||||||
if (*allowedAmount < 0)
|
|
||||||
*allowedAmount = 0;
|
if (amount > 0)
|
||||||
} else
|
{
|
||||||
{
|
if (c->GetStartTime()-clip->GetEndTime() < *allowedAmount)
|
||||||
if (c->GetEndTime()-clip->GetStartTime() > *allowedAmount)
|
*allowedAmount = c->GetStartTime()-clip->GetEndTime();
|
||||||
*allowedAmount = c->GetEndTime()-clip->GetStartTime();
|
if (*allowedAmount < 0)
|
||||||
if (*allowedAmount > 0)
|
*allowedAmount = 0;
|
||||||
*allowedAmount = 0;
|
} else
|
||||||
|
{
|
||||||
|
if (c->GetEndTime()-clip->GetStartTime() > *allowedAmount)
|
||||||
|
*allowedAmount = c->GetEndTime()-clip->GetStartTime();
|
||||||
|
if (*allowedAmount > 0)
|
||||||
|
*allowedAmount = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2249,7 +2260,7 @@ bool WaveTrack::CanOffsetClip(WaveClip* clip, double amount,
|
|||||||
|
|
||||||
// Check if the NEW calculated amount would not violate
|
// Check if the NEW calculated amount would not violate
|
||||||
// any other constraint
|
// any other constraint
|
||||||
if (!CanOffsetClip(clip, *allowedAmount, NULL)) {
|
if (!CanOffsetClips(clips, *allowedAmount, nullptr)) {
|
||||||
*allowedAmount = 0; // play safe and don't allow anything
|
*allowedAmount = 0; // play safe and don't allow anything
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -465,11 +465,16 @@ private:
|
|||||||
WaveClipPointers SortedClipArray();
|
WaveClipPointers SortedClipArray();
|
||||||
WaveClipConstPointers SortedClipArray() const;
|
WaveClipConstPointers SortedClipArray() const;
|
||||||
|
|
||||||
// Before calling 'Offset' on a clip, use this function to see if the
|
//! Decide whether the clips could be offset (and inserted) together without overlapping other clips
|
||||||
// offsetting is allowed with respect to the other clips in this track.
|
/*!
|
||||||
// This function can optionally return the amount that is allowed for offsetting
|
@return true if possible to offset by `(allowedAmount ? *allowedAmount : amount)`
|
||||||
// in this direction maximally.
|
*/
|
||||||
bool CanOffsetClip(WaveClip* clip, double amount, double *allowedAmount=NULL);
|
bool CanOffsetClips(
|
||||||
|
const std::vector<WaveClip*> &clips, //!< not necessarily in this track
|
||||||
|
double amount, //!< signed
|
||||||
|
double *allowedAmount = nullptr /*!<
|
||||||
|
[out] if null, test exact amount only; else, largest (in magnitude) possible offset with same sign */
|
||||||
|
);
|
||||||
|
|
||||||
// Before moving a clip into a track (or inserting a clip), use this
|
// Before moving a clip into a track (or inserting a clip), use this
|
||||||
// function to see if the times are valid (i.e. don't overlap with
|
// function to see if the times are valid (i.e. don't overlap with
|
||||||
|
@ -659,8 +659,7 @@ double DoClipMove
|
|||||||
auto desiredT0 = viewInfo.OffsetTimeByPixels( t0, ( right ? 1 : -1 ) );
|
auto desiredT0 = viewInfo.OffsetTimeByPixels( t0, ( right ? 1 : -1 ) );
|
||||||
auto desiredSlideAmount = pShifter->HintOffsetLarger( desiredT0 - t0 );
|
auto desiredSlideAmount = pShifter->HintOffsetLarger( desiredT0 - t0 );
|
||||||
|
|
||||||
auto hSlideAmount =
|
auto hSlideAmount = state.DoSlideHorizontal( desiredSlideAmount );
|
||||||
state.DoSlideHorizontal( desiredSlideAmount, trackList );
|
|
||||||
|
|
||||||
// update t0 and t1. There is the possibility that the updated
|
// update t0 and t1. There is the possibility that the updated
|
||||||
// t0 may no longer be within the clip due to rounding errors,
|
// t0 may no longer be within the clip due to rounding errors,
|
||||||
|
@ -1364,6 +1364,26 @@ public:
|
|||||||
desiredOffset *= -1;
|
desiredOffset *= -1;
|
||||||
return desiredOffset;
|
return desiredOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double QuantizeOffset( double desiredOffset ) override
|
||||||
|
{
|
||||||
|
const auto rate = mpTrack->GetRate();
|
||||||
|
// set it to a sample point
|
||||||
|
return rint(desiredOffset * rate) / rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
double AdjustOffsetSmaller(double desiredOffset) override
|
||||||
|
{
|
||||||
|
std::vector< WaveClip * > movingClips;
|
||||||
|
for ( auto &interval : MovingIntervals() ) {
|
||||||
|
auto data =
|
||||||
|
static_cast<WaveTrack::IntervalData*>( interval.Extra() );
|
||||||
|
movingClips.push_back(data->GetClip().get());
|
||||||
|
}
|
||||||
|
double newAmount = 0;
|
||||||
|
(void) mpTrack->CanOffsetClips(movingClips, desiredOffset, &newAmount);
|
||||||
|
return newAmount;
|
||||||
|
}
|
||||||
|
|
||||||
Intervals Detach() override
|
Intervals Detach() override
|
||||||
{
|
{
|
||||||
@ -1420,6 +1440,15 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DoHorizontalOffset( double offset ) override
|
||||||
|
{
|
||||||
|
for ( auto &interval : MovingIntervals() ) {
|
||||||
|
auto data =
|
||||||
|
static_cast<WaveTrack::IntervalData*>( interval.Extra() );
|
||||||
|
data->GetClip()->Offset( offset );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<WaveTrack> mpTrack;
|
std::shared_ptr<WaveTrack> mpTrack;
|
||||||
|
|
||||||
|
@ -16,33 +16,19 @@ Paul Licameli split from TrackPanel.cpp
|
|||||||
#include "TrackView.h"
|
#include "TrackView.h"
|
||||||
#include "../../AColor.h"
|
#include "../../AColor.h"
|
||||||
#include "../../HitTestResult.h"
|
#include "../../HitTestResult.h"
|
||||||
#include "../../NoteTrack.h"
|
|
||||||
#include "../../ProjectAudioIO.h"
|
#include "../../ProjectAudioIO.h"
|
||||||
#include "../../ProjectHistory.h"
|
#include "../../ProjectHistory.h"
|
||||||
#include "../../ProjectSettings.h"
|
#include "../../ProjectSettings.h"
|
||||||
#include "../../RefreshCode.h"
|
#include "../../RefreshCode.h"
|
||||||
#include "../../Snap.h"
|
#include "../../Snap.h"
|
||||||
|
#include "../../Track.h"
|
||||||
#include "../../TrackArtist.h"
|
#include "../../TrackArtist.h"
|
||||||
#include "../../TrackPanelDrawingContext.h"
|
#include "../../TrackPanelDrawingContext.h"
|
||||||
#include "../../TrackPanelMouseEvent.h"
|
#include "../../TrackPanelMouseEvent.h"
|
||||||
#include "../../UndoManager.h"
|
#include "../../UndoManager.h"
|
||||||
#include "../../WaveClip.h"
|
|
||||||
#include "../../ViewInfo.h"
|
#include "../../ViewInfo.h"
|
||||||
#include "../../WaveTrack.h"
|
|
||||||
#include "../../../images/Cursors.h"
|
#include "../../../images/Cursors.h"
|
||||||
|
|
||||||
TrackClip::TrackClip(Track *t, WaveClip *c)
|
|
||||||
{
|
|
||||||
track = origTrack = t;
|
|
||||||
dstTrack = NULL;
|
|
||||||
clip = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
TrackClip::~TrackClip()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
TimeShiftHandle::TimeShiftHandle
|
TimeShiftHandle::TimeShiftHandle
|
||||||
( const std::shared_ptr<Track> &pTrack, bool gripHit )
|
( const std::shared_ptr<Track> &pTrack, bool gripHit )
|
||||||
: mGripHit{ gripHit }
|
: mGripHit{ gripHit }
|
||||||
@ -115,88 +101,15 @@ TimeShiftHandle::~TimeShiftHandle()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
void ClipMoveState::DoHorizontalOffset( double offset )
|
||||||
{
|
{
|
||||||
// Adds a track's clips to state.capturedClipArray within a specified time
|
if ( !shifters.empty() ) {
|
||||||
void AddClipsToCaptured
|
for ( auto &pair : shifters )
|
||||||
( ClipMoveState &state, Track *t, double t0, double t1 )
|
pair.second->DoHorizontalOffset( offset );
|
||||||
{
|
|
||||||
auto &clips = state.capturedClipArray;
|
|
||||||
t->TypeSwitch(
|
|
||||||
[&](WaveTrack *wt) {
|
|
||||||
for(const auto &clip: wt->GetClips())
|
|
||||||
if ( ! clip->IsClipStartAfterClip(t0) && ! clip->BeforeClip(t1) &&
|
|
||||||
// Avoid getting clips that were already captured
|
|
||||||
! std::any_of( clips.begin(), clips.end(),
|
|
||||||
[&](const TrackClip &c) { return c.clip == clip.get(); } ) )
|
|
||||||
clips.emplace_back( t, clip.get() );
|
|
||||||
},
|
|
||||||
#ifdef USE_MIDI
|
|
||||||
[&](NoteTrack *, const Track::Fallthrough &fallthrough){
|
|
||||||
// do not add NoteTrack if the data is outside of time bounds
|
|
||||||
if (t->GetEndTime() < t0 || t->GetStartTime() > t1)
|
|
||||||
return;
|
|
||||||
else
|
|
||||||
fallthrough();
|
|
||||||
},
|
|
||||||
#endif
|
|
||||||
[&](Track *t) {
|
|
||||||
// This handles label tracks rather heavy-handedly --
|
|
||||||
// it would be nice to
|
|
||||||
// treat individual labels like clips
|
|
||||||
|
|
||||||
// Avoid adding a track twice
|
|
||||||
if( !std::any_of( clips.begin(), clips.end(),
|
|
||||||
[&](const TrackClip &c) { return c.track == t; } ) ) {
|
|
||||||
clips.emplace_back( t, nullptr );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
// Helper for the above, adds a track's clips to capturedClipArray (eliminates
|
for (auto channel : TrackList::Channels( mCapturedTrack.get() ))
|
||||||
// duplication of this logic)
|
channel->Offset( offset );
|
||||||
void AddClipsToCaptured
|
|
||||||
( ClipMoveState &state, const ViewInfo &viewInfo,
|
|
||||||
Track *t )
|
|
||||||
{
|
|
||||||
AddClipsToCaptured( state, t, viewInfo.selectedRegion.t0(),
|
|
||||||
viewInfo.selectedRegion.t1() );
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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 : TrackList::Channels( pTrack ))
|
|
||||||
channel->Offset( offset );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,6 +122,7 @@ void TrackShifter::UnfixIntervals(
|
|||||||
if ( pred( *iter) ) {
|
if ( pred( *iter) ) {
|
||||||
mMoving.push_back( std::move( *iter ) );
|
mMoving.push_back( std::move( *iter ) );
|
||||||
iter = mFixed.erase( iter );
|
iter = mFixed.erase( iter );
|
||||||
|
mAllFixed = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
++iter;
|
++iter;
|
||||||
@ -219,6 +133,7 @@ void TrackShifter::UnfixAll()
|
|||||||
{
|
{
|
||||||
std::move( mFixed.begin(), mFixed.end(), std::back_inserter(mMoving) );
|
std::move( mFixed.begin(), mFixed.end(), std::back_inserter(mMoving) );
|
||||||
mFixed = Intervals{};
|
mFixed = Intervals{};
|
||||||
|
mAllFixed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TrackShifter::SelectInterval( const TrackInterval & )
|
void TrackShifter::SelectInterval( const TrackInterval & )
|
||||||
@ -239,6 +154,16 @@ double TrackShifter::HintOffsetLarger(double desiredOffset)
|
|||||||
return desiredOffset;
|
return desiredOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double TrackShifter::QuantizeOffset(double desiredOffset)
|
||||||
|
{
|
||||||
|
return desiredOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
double TrackShifter::AdjustOffsetSmaller(double desiredOffset)
|
||||||
|
{
|
||||||
|
return desiredOffset;
|
||||||
|
}
|
||||||
|
|
||||||
bool TrackShifter::MayMigrateTo(Track &)
|
bool TrackShifter::MayMigrateTo(Track &)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -296,6 +221,12 @@ bool TrackShifter::FinishMigration()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TrackShifter::DoHorizontalOffset( double offset )
|
||||||
|
{
|
||||||
|
if (!AllFixed())
|
||||||
|
GetTrack().Offset( offset );
|
||||||
|
}
|
||||||
|
|
||||||
void TrackShifter::InitIntervals()
|
void TrackShifter::InitIntervals()
|
||||||
{
|
{
|
||||||
mMoving.clear();
|
mMoving.clear();
|
||||||
@ -333,13 +264,7 @@ void ClipMoveState::Init(
|
|||||||
const ViewInfo &viewInfo,
|
const ViewInfo &viewInfo,
|
||||||
TrackList &trackList, bool syncLocked )
|
TrackList &trackList, bool syncLocked )
|
||||||
{
|
{
|
||||||
capturedClipArray.clear();
|
|
||||||
shifters.clear();
|
shifters.clear();
|
||||||
auto cleanup = finally([&]{
|
|
||||||
// In transition, this class holds two representations of what to shift.
|
|
||||||
// Be sure each is filled only if the other is.
|
|
||||||
wxASSERT( capturedClipArray.empty() == shifters.empty() );
|
|
||||||
});
|
|
||||||
|
|
||||||
auto &state = *this;
|
auto &state = *this;
|
||||||
state.mCapturedTrack = capturedTrack.SharedPointer();
|
state.mCapturedTrack = capturedTrack.SharedPointer();
|
||||||
@ -353,15 +278,6 @@ void ClipMoveState::Init(
|
|||||||
|
|
||||||
const bool capturedAClip =
|
const bool capturedAClip =
|
||||||
pHit && !pHit->MovingIntervals().empty();
|
pHit && !pHit->MovingIntervals().empty();
|
||||||
if ( capturedAClip ) {
|
|
||||||
// There is still some code special to WaveTracks here that
|
|
||||||
// needs to go elsewhere
|
|
||||||
auto &interval = pHit->MovingIntervals()[0];
|
|
||||||
auto pInfo =
|
|
||||||
dynamic_cast<WaveTrack::IntervalData*>(interval.Extra());
|
|
||||||
if ( pInfo )
|
|
||||||
state.capturedClip = pInfo->GetClip().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
state.shifters[&capturedTrack] = std::move( pHit );
|
state.shifters[&capturedTrack] = std::move( pHit );
|
||||||
|
|
||||||
@ -372,65 +288,6 @@ void ClipMoveState::Init(
|
|||||||
pShifter = MakeTrackShifter::Call( *track );
|
pShifter = MakeTrackShifter::Call( *track );
|
||||||
}
|
}
|
||||||
|
|
||||||
// The captured clip is the focus, but we need to create a list
|
|
||||||
// of all clips that have to move, also...
|
|
||||||
|
|
||||||
// First, if click was in selection, capture selected clips; otherwise
|
|
||||||
// just the clicked-on clip
|
|
||||||
if ( state.movingSelection )
|
|
||||||
// All selected tracks may move some intervals
|
|
||||||
for (auto t : trackList.Selected())
|
|
||||||
AddClipsToCaptured( state, viewInfo, t );
|
|
||||||
else {
|
|
||||||
// Move intervals only of the chosen channel group
|
|
||||||
state.capturedClipArray.push_back
|
|
||||||
(TrackClip( &capturedTrack, state.capturedClip ));
|
|
||||||
|
|
||||||
if (state.capturedClip) {
|
|
||||||
// Check for other channels
|
|
||||||
auto wt = static_cast<WaveTrack*>(&capturedTrack);
|
|
||||||
for ( auto channel : TrackList::Channels( wt ).Excluding( wt ) )
|
|
||||||
if (WaveClip *const clip = FindClipAtTime(channel, clickTime))
|
|
||||||
state.capturedClipArray.push_back(TrackClip(channel, clip));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now, if sync-lock is enabled, capture any clip that's linked to a
|
|
||||||
// captured clip.
|
|
||||||
if ( syncLocked ) {
|
|
||||||
// Sync lock propagation of unfixing of intervals
|
|
||||||
// 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 ( trackClip.clip ) {
|
|
||||||
// Iterate over sync-lock group tracks.
|
|
||||||
for (auto t : TrackList::SyncLockGroup( trackClip.track ))
|
|
||||||
AddClipsToCaptured(state, t,
|
|
||||||
trackClip.clip->GetStartTime(),
|
|
||||||
trackClip.clip->GetEndTime() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifdef USE_MIDI
|
|
||||||
{
|
|
||||||
// Capture additional clips from NoteTracks
|
|
||||||
trackClip.track->TypeSwitch( [&](NoteTrack *nt) {
|
|
||||||
// Iterate over sync-lock group tracks.
|
|
||||||
for (auto t : TrackList::SyncLockGroup(nt))
|
|
||||||
AddClipsToCaptured
|
|
||||||
( state, t, nt->GetStartTime(), nt->GetEndTime() );
|
|
||||||
});
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Analogy of the steps above, but with TrackShifters, follows below
|
// Analogy of the steps above, but with TrackShifters, follows below
|
||||||
|
|
||||||
if ( state.movingSelection ) {
|
if ( state.movingSelection ) {
|
||||||
@ -513,63 +370,42 @@ const TrackInterval *ClipMoveState::CapturedInterval() const
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
double ClipMoveState::DoSlideHorizontal(
|
double ClipMoveState::DoSlideHorizontal( double desiredSlideAmount )
|
||||||
double desiredSlideAmount, TrackList &trackList )
|
|
||||||
{
|
{
|
||||||
auto &state = *this;
|
auto &state = *this;
|
||||||
auto &capturedTrack = *state.mCapturedTrack;
|
auto &capturedTrack = *state.mCapturedTrack;
|
||||||
state.hSlideAmount = desiredSlideAmount;
|
|
||||||
|
|
||||||
// Given a signed slide distance, move clips, but subject to constraint of
|
// Given a signed slide distance, move clips, but subject to constraint of
|
||||||
// non-overlapping with other clips, so the distance may be adjusted toward
|
// non-overlapping with other clips, so the distance may be adjusted toward
|
||||||
// zero.
|
// zero.
|
||||||
if ( state.capturedClipArray.size() )
|
if ( !state.shifters.empty() ) {
|
||||||
{
|
double initialAllowed = 0;
|
||||||
double allowed;
|
|
||||||
double initialAllowed;
|
|
||||||
double safeBigDistance = 1000 + 2.0 * ( trackList.GetEndTime() -
|
|
||||||
trackList.GetStartTime() );
|
|
||||||
|
|
||||||
do { // loop to compute allowed, does not actually move anything yet
|
do { // loop to compute allowed, does not actually move anything yet
|
||||||
initialAllowed = state.hSlideAmount;
|
initialAllowed = desiredSlideAmount;
|
||||||
|
|
||||||
for ( auto &trackClip : state.capturedClipArray ) {
|
for (auto &pair : shifters) {
|
||||||
if (const auto clip = trackClip.clip) {
|
auto newAmount = pair.second->AdjustOffsetSmaller( desiredSlideAmount );
|
||||||
// only audio clips are used to compute allowed
|
if ( desiredSlideAmount != newAmount ) {
|
||||||
const auto track = static_cast<WaveTrack *>( trackClip.track );
|
if ( newAmount * desiredSlideAmount < 0 ||
|
||||||
|
fabs(newAmount) > fabs(desiredSlideAmount) ) {
|
||||||
// Move all other selected clips totally out of the way
|
wxASSERT( false ); // AdjustOffsetSmaller didn't honor postcondition!
|
||||||
// temporarily because they're all moving together and
|
newAmount = 0; // Be sure the loop progresses to termination!
|
||||||
// we want to find out if OTHER clips are in the way,
|
|
||||||
// not one of the moving ones
|
|
||||||
DoOffset( state, nullptr, -safeBigDistance, clip );
|
|
||||||
auto cleanup = finally( [&]
|
|
||||||
{ DoOffset( state, nullptr, safeBigDistance, clip ); } );
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
desiredSlideAmount = newAmount;
|
||||||
|
state.snapLeft = state.snapRight = -1; // see bug 1067
|
||||||
}
|
}
|
||||||
|
if (newAmount == 0)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} while ( state.hSlideAmount != initialAllowed );
|
} while ( desiredSlideAmount != initialAllowed );
|
||||||
|
|
||||||
// finally, here is where clips are moved
|
|
||||||
if ( state.hSlideAmount != 0.0 )
|
|
||||||
DoOffset( state, nullptr, state.hSlideAmount );
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
// For Shift key down, or
|
|
||||||
// For non wavetracks, specifically label tracks ...
|
|
||||||
DoOffset( state, &capturedTrack, state.hSlideAmount );
|
|
||||||
|
|
||||||
return state.hSlideAmount;
|
// Whether moving intervals or a whole track,
|
||||||
|
// finally, here is where clips are moved
|
||||||
|
if ( desiredSlideAmount != 0.0 )
|
||||||
|
state.DoHorizontalOffset( desiredSlideAmount );
|
||||||
|
|
||||||
|
return (state.hSlideAmount = desiredSlideAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -621,9 +457,6 @@ UIHandle::Result TimeShiftHandle::Click
|
|||||||
const double clickTime =
|
const double clickTime =
|
||||||
viewInfo.PositionToTime(event.m_x, rect.x);
|
viewInfo.PositionToTime(event.m_x, rect.x);
|
||||||
|
|
||||||
mClipMoveState.capturedClip = NULL;
|
|
||||||
mClipMoveState.capturedClipArray.clear();
|
|
||||||
|
|
||||||
bool captureClips = false;
|
bool captureClips = false;
|
||||||
|
|
||||||
auto pShifter = MakeTrackShifter::Call( *pTrack );
|
auto pShifter = MakeTrackShifter::Call( *pTrack );
|
||||||
@ -677,8 +510,9 @@ namespace {
|
|||||||
SnapManager *pSnapManager,
|
SnapManager *pSnapManager,
|
||||||
bool slideUpDownOnly, bool snapPreferRightEdge,
|
bool slideUpDownOnly, bool snapPreferRightEdge,
|
||||||
ClipMoveState &state,
|
ClipMoveState &state,
|
||||||
Track &capturedTrack, Track &track )
|
Track &track )
|
||||||
{
|
{
|
||||||
|
auto &capturedTrack = *state.mCapturedTrack;
|
||||||
if (slideUpDownOnly)
|
if (slideUpDownOnly)
|
||||||
return 0.0;
|
return 0.0;
|
||||||
else {
|
else {
|
||||||
@ -687,11 +521,9 @@ namespace {
|
|||||||
viewInfo.PositionToTime(state.mMouseClickX);
|
viewInfo.PositionToTime(state.mMouseClickX);
|
||||||
double clipLeft = 0, clipRight = 0;
|
double clipLeft = 0, clipRight = 0;
|
||||||
|
|
||||||
track.TypeSwitch( [&](WaveTrack *mtw){
|
if (!state.shifters.empty())
|
||||||
const double rate = mtw->GetRate();
|
desiredSlideAmount =
|
||||||
// set it to a sample point
|
state.shifters[ &track ]->QuantizeOffset( desiredSlideAmount );
|
||||||
desiredSlideAmount = rint(desiredSlideAmount * rate) / rate;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Adjust desiredSlideAmount using SnapManager
|
// Adjust desiredSlideAmount using SnapManager
|
||||||
if (pSnapManager) {
|
if (pSnapManager) {
|
||||||
@ -791,12 +623,6 @@ namespace {
|
|||||||
++iter; // Safe to increment TrackIter even at end of range
|
++iter; // Safe to increment TrackIter even at end of range
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record the correspondence in TrackClip
|
|
||||||
for ( auto &trackClip: state.capturedClipArray )
|
|
||||||
if ( trackClip.clip )
|
|
||||||
trackClip.dstTrack =
|
|
||||||
dynamic_cast<WaveTrack*>(correspondence[ trackClip.track ]);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -854,26 +680,12 @@ namespace {
|
|||||||
|
|
||||||
void Fail()
|
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);
|
|
||||||
failed = true;
|
failed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reinsert(
|
void Reinsert(
|
||||||
std::unordered_map< Track*, Track* > &correspondence )
|
std::unordered_map< Track*, Track* > &correspondence )
|
||||||
{
|
{
|
||||||
// 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;
|
|
||||||
trackClip.track = dstTrack;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &pair : detached) {
|
for (auto &pair : detached) {
|
||||||
auto pTrack = pair.first;
|
auto pTrack = pair.first;
|
||||||
if (!failed && correspondence.count(pTrack))
|
if (!failed && correspondence.count(pTrack))
|
||||||
@ -970,8 +782,7 @@ UIHandle::Result TimeShiftHandle::Drag
|
|||||||
// Start by undoing the current slide amount; everything
|
// Start by undoing the current slide amount; everything
|
||||||
// happens relative to the original horizontal position of
|
// happens relative to the original horizontal position of
|
||||||
// each clip...
|
// each clip...
|
||||||
DoOffset(
|
mClipMoveState.DoHorizontalOffset( -mClipMoveState.hSlideAmount );
|
||||||
mClipMoveState, mClipMoveState.mCapturedTrack.get(), -mClipMoveState.hSlideAmount );
|
|
||||||
|
|
||||||
if ( mClipMoveState.movingSelection ) {
|
if ( mClipMoveState.movingSelection ) {
|
||||||
// Slide the selection, too
|
// Slide the selection, too
|
||||||
@ -982,34 +793,29 @@ UIHandle::Result TimeShiftHandle::Drag
|
|||||||
double desiredSlideAmount =
|
double desiredSlideAmount =
|
||||||
FindDesiredSlideAmount( viewInfo, mRect.x, event, mSnapManager.get(),
|
FindDesiredSlideAmount( viewInfo, mRect.x, event, mSnapManager.get(),
|
||||||
mSlideUpDownOnly, mSnapPreferRightEdge, mClipMoveState,
|
mSlideUpDownOnly, mSnapPreferRightEdge, mClipMoveState,
|
||||||
*mClipMoveState.mCapturedTrack, *pTrack );
|
*pTrack );
|
||||||
|
|
||||||
// Scroll during vertical drag.
|
// Scroll during vertical drag.
|
||||||
// If the mouse is over a track that isn't the captured track,
|
// If the mouse is over a track that isn't the captured track,
|
||||||
// decide which tracks the captured clips should go to.
|
// decide which tracks the captured clips should go to.
|
||||||
// EnsureVisible(pTrack); //vvv Gale says this has problems on Linux, per bug 393 thread. Revert for 2.0.2.
|
// EnsureVisible(pTrack); //vvv Gale says this has problems on Linux, per bug 393 thread. Revert for 2.0.2.
|
||||||
bool slidVertically = (
|
bool slidVertically = (
|
||||||
mClipMoveState.capturedClip &&
|
|
||||||
pTrack != mClipMoveState.mCapturedTrack
|
pTrack != mClipMoveState.mCapturedTrack
|
||||||
/* && !mCapturedClipIsSelection*/
|
/* && !mCapturedClipIsSelection*/
|
||||||
&& pTrack->TypeSwitch<bool>( [&] (WaveTrack *) {
|
&& DoSlideVertical( viewInfo, event.m_x, mClipMoveState,
|
||||||
if ( DoSlideVertical( viewInfo, event.m_x, mClipMoveState,
|
trackList, *pTrack, desiredSlideAmount ) );
|
||||||
trackList, *pTrack, desiredSlideAmount ) ) {
|
if (slidVertically)
|
||||||
mClipMoveState.mCapturedTrack = pTrack;
|
{
|
||||||
mDidSlideVertically = true;
|
mClipMoveState.mCapturedTrack = pTrack;
|
||||||
return true;
|
mDidSlideVertically = true;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return false;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
if (desiredSlideAmount == 0.0)
|
if (desiredSlideAmount == 0.0)
|
||||||
return RefreshAll;
|
return RefreshAll;
|
||||||
|
|
||||||
// Note that mouse dragging doesn't use TrackShifter::HintOffsetLarger()
|
// Note that mouse dragging doesn't use TrackShifter::HintOffsetLarger()
|
||||||
|
|
||||||
mClipMoveState.DoSlideHorizontal( desiredSlideAmount, trackList );
|
mClipMoveState.DoSlideHorizontal( desiredSlideAmount );
|
||||||
|
|
||||||
if (mClipMoveState.movingSelection) {
|
if (mClipMoveState.movingSelection) {
|
||||||
// Slide the selection, too
|
// Slide the selection, too
|
||||||
|
@ -75,6 +75,19 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual double HintOffsetLarger( double desiredOffset );
|
virtual double HintOffsetLarger( double desiredOffset );
|
||||||
|
|
||||||
|
//! Given amount to shift by horizontally, do any preferred rounding, before placement constraint checks
|
||||||
|
/*! Default implementation returns argument */
|
||||||
|
virtual double QuantizeOffset( double desiredOffset );
|
||||||
|
|
||||||
|
//! Given amount to shift by horizontally, maybe adjust it toward zero to meet placement constraints
|
||||||
|
/*!
|
||||||
|
Default implementation returns the argument
|
||||||
|
@post `fabs(r) <= fabs(desiredOffset)`
|
||||||
|
@post `r * desiredOffset >= 0` (i.e. signs are not opposite)
|
||||||
|
@post (where `r` is return value)
|
||||||
|
*/
|
||||||
|
virtual double AdjustOffsetSmaller( double desiredOffset );
|
||||||
|
|
||||||
//! Whether intervals may migrate to the other track, not yet checking all placement constraints */
|
//! Whether intervals may migrate to the other track, not yet checking all placement constraints */
|
||||||
/*! Default implementation returns false */
|
/*! Default implementation returns false */
|
||||||
virtual bool MayMigrateTo( Track &otherTrack );
|
virtual bool MayMigrateTo( Track &otherTrack );
|
||||||
@ -109,6 +122,10 @@ public:
|
|||||||
Default implementation does nothing and returns true */
|
Default implementation does nothing and returns true */
|
||||||
virtual bool FinishMigration();
|
virtual bool FinishMigration();
|
||||||
|
|
||||||
|
//! Shift all moving intervals horizontally
|
||||||
|
//! Default moves the whole track, provided `!AllFixed()`; else does nothing
|
||||||
|
virtual void DoHorizontalOffset( double offset );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/*! Unfix any of the intervals that intersect the given one; may be useful to override `SelectInterval()` */
|
/*! Unfix any of the intervals that intersect the given one; may be useful to override `SelectInterval()` */
|
||||||
void CommonSelectInterval( const TrackInterval &interval );
|
void CommonSelectInterval( const TrackInterval &interval );
|
||||||
@ -122,8 +139,16 @@ protected:
|
|||||||
/*! This can't be called by the base class constructor, when GetTrack() isn't yet callable */
|
/*! This can't be called by the base class constructor, when GetTrack() isn't yet callable */
|
||||||
void InitIntervals();
|
void InitIntervals();
|
||||||
|
|
||||||
|
bool AllFixed() const {
|
||||||
|
return mAllFixed && mMoving.empty();
|
||||||
|
}
|
||||||
|
|
||||||
Intervals mFixed;
|
Intervals mFixed;
|
||||||
Intervals mMoving;
|
Intervals mMoving;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mAllFixed = true; /*!<
|
||||||
|
Becomes false after `UnfixAll()`, even if there are no intervals, or if any one interval was unfixed */
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Used in default of other reimplementations to shift any track as a whole, invoking Track::Offset()
|
//! Used in default of other reimplementations to shift any track as a whole, invoking Track::Offset()
|
||||||
@ -147,25 +172,6 @@ using MakeTrackShifter = AttachedVirtualFunction<
|
|||||||
MakeTrackShifterTag, std::unique_ptr<TrackShifter>, Track>;
|
MakeTrackShifterTag, std::unique_ptr<TrackShifter>, Track>;
|
||||||
|
|
||||||
class ViewInfo;
|
class ViewInfo;
|
||||||
class WaveClip;
|
|
||||||
class WaveTrack;
|
|
||||||
|
|
||||||
class TrackClip
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TrackClip(Track *t, WaveClip *c);
|
|
||||||
|
|
||||||
~TrackClip();
|
|
||||||
|
|
||||||
Track *track;
|
|
||||||
Track *origTrack;
|
|
||||||
WaveClip *clip;
|
|
||||||
|
|
||||||
// These fields are used only during time-shift dragging
|
|
||||||
WaveTrack *dstTrack;
|
|
||||||
};
|
|
||||||
|
|
||||||
using TrackClipArray = std::vector <TrackClip>;
|
|
||||||
|
|
||||||
struct ClipMoveState {
|
struct ClipMoveState {
|
||||||
using ShifterMap = std::unordered_map<Track*, std::unique_ptr<TrackShifter>>;
|
using ShifterMap = std::unordered_map<Track*, std::unique_ptr<TrackShifter>>;
|
||||||
@ -183,29 +189,27 @@ struct ClipMoveState {
|
|||||||
/*! Pointer may be invalidated by operations on the associated TrackShifter */
|
/*! Pointer may be invalidated by operations on the associated TrackShifter */
|
||||||
const TrackInterval *CapturedInterval() const;
|
const TrackInterval *CapturedInterval() const;
|
||||||
|
|
||||||
|
//! Do sliding of tracks and intervals, maybe adjusting the offset
|
||||||
/*! @return actual slide amount, maybe adjusted toward zero from desired */
|
/*! @return actual slide amount, maybe adjusted toward zero from desired */
|
||||||
double DoSlideHorizontal( double desiredSlideAmount, TrackList &trackList );
|
double DoSlideHorizontal( double desiredSlideAmount );
|
||||||
|
|
||||||
|
//! Offset tracks or intervals horizontally, without adjusting the offset
|
||||||
|
void DoHorizontalOffset( double offset );
|
||||||
|
|
||||||
std::shared_ptr<Track> mCapturedTrack;
|
std::shared_ptr<Track> mCapturedTrack;
|
||||||
|
|
||||||
// non-NULL only if click was in a WaveTrack and without Shift key:
|
|
||||||
WaveClip *capturedClip {};
|
|
||||||
|
|
||||||
bool movingSelection {};
|
bool movingSelection {};
|
||||||
double hSlideAmount {};
|
double hSlideAmount {};
|
||||||
ShifterMap shifters;
|
ShifterMap shifters;
|
||||||
TrackClipArray capturedClipArray {};
|
|
||||||
wxInt64 snapLeft { -1 }, snapRight { -1 };
|
wxInt64 snapLeft { -1 }, snapRight { -1 };
|
||||||
|
|
||||||
int mMouseClickX{};
|
int mMouseClickX{};
|
||||||
|
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
capturedClip = nullptr;
|
|
||||||
movingSelection = false;
|
movingSelection = false;
|
||||||
hSlideAmount = 0;
|
hSlideAmount = 0;
|
||||||
shifters.clear();
|
shifters.clear();
|
||||||
capturedClipArray.clear();
|
|
||||||
snapLeft = snapRight = -1;
|
snapLeft = snapRight = -1;
|
||||||
mMouseClickX = 0;
|
mMouseClickX = 0;
|
||||||
}
|
}
|
||||||
@ -226,7 +230,7 @@ public:
|
|||||||
bool IsGripHit() const { return mGripHit; }
|
bool IsGripHit() const { return mGripHit; }
|
||||||
std::shared_ptr<Track> GetTrack() const = delete;
|
std::shared_ptr<Track> GetTrack() const = delete;
|
||||||
|
|
||||||
// Try to move clips from one WaveTrack to another, before also moving
|
// Try to move clips from one track to another, before also moving
|
||||||
// by some horizontal amount, which may be slightly adjusted to fit the
|
// by some horizontal amount, which may be slightly adjusted to fit the
|
||||||
// destination tracks.
|
// destination tracks.
|
||||||
static bool DoSlideVertical(
|
static bool DoSlideVertical(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user