mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-16 08:09:32 +02:00
Move more time-shifting functions out of TrackPanel.cpp
This commit is contained in:
parent
251976d93d
commit
ef38af71dd
@ -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 );
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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); }
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user