mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-17 00:20:06 +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 );
|
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 )
|
void AudacityProject::DoClipLeftOrRight(bool right, bool keyUp )
|
||||||
{
|
{
|
||||||
if (keyUp) {
|
if (keyUp) {
|
||||||
@ -3367,7 +3423,7 @@ void AudacityProject::DoClipLeftOrRight(bool right, bool keyUp )
|
|||||||
|
|
||||||
auto &panel = *GetTrackPanel();
|
auto &panel = *GetTrackPanel();
|
||||||
|
|
||||||
auto amount = TrackPanel::OnClipMove
|
auto amount = OnClipMove
|
||||||
( mViewInfo, panel.GetFocusedTrack(),
|
( mViewInfo, panel.GetFocusedTrack(),
|
||||||
*GetTracks(), IsSyncLocked(), right );
|
*GetTracks(), IsSyncLocked(), right );
|
||||||
|
|
||||||
|
@ -164,6 +164,10 @@ void OnSelContractLeft(const wxEvent * evt);
|
|||||||
void OnSelContractRight(const wxEvent * evt);
|
void OnSelContractRight(const wxEvent * evt);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static double OnClipMove
|
||||||
|
(ViewInfo &viewInfo, Track *track,
|
||||||
|
TrackList &trackList, bool syncLocked, bool right);
|
||||||
|
|
||||||
void DoClipLeftOrRight(bool right, bool keyUp );
|
void DoClipLeftOrRight(bool right, bool keyUp );
|
||||||
void OnClipLeft(const wxEvent* evt);
|
void OnClipLeft(const wxEvent* evt);
|
||||||
void OnClipRight(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
|
/// Determines if drag zooming is active
|
||||||
bool TrackPanel::IsDragZooming(int zoomStart, int zoomEnd)
|
bool TrackPanel::IsDragZooming(int zoomStart, int zoomEnd)
|
||||||
{
|
{
|
||||||
|
@ -21,8 +21,6 @@
|
|||||||
#include "SelectedRegion.h"
|
#include "SelectedRegion.h"
|
||||||
#include "WaveTrackLocation.h"
|
#include "WaveTrackLocation.h"
|
||||||
|
|
||||||
#include "Track.h"
|
|
||||||
#include "Snap.h"
|
|
||||||
#include "widgets/OverlayPanel.h"
|
#include "widgets/OverlayPanel.h"
|
||||||
|
|
||||||
#include "SelectionState.h"
|
#include "SelectionState.h"
|
||||||
@ -256,27 +254,6 @@ private:
|
|||||||
const int DragThreshold = 3;// Anything over 3 pixels is a drag, else a click.
|
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 {
|
class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -371,10 +348,6 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
|
|||||||
// (ignoring any fisheye)
|
// (ignoring any fisheye)
|
||||||
virtual double GetScreenEndTime() const;
|
virtual double GetScreenEndTime() const;
|
||||||
|
|
||||||
static double OnClipMove
|
|
||||||
(ViewInfo &viewInfo, Track *track,
|
|
||||||
TrackList &trackList, bool syncLocked, bool right);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual MixerBoard* GetMixerBoard();
|
virtual MixerBoard* GetMixerBoard();
|
||||||
/** @brief Populates the track pop-down menu with the common set of
|
/** @brief Populates the track pop-down menu with the common set of
|
||||||
@ -503,19 +476,6 @@ protected:
|
|||||||
virtual void ForwardEventToWaveTrackEnvelope(wxMouseEvent & event);
|
virtual void ForwardEventToWaveTrackEnvelope(wxMouseEvent & event);
|
||||||
virtual void ForwardEventToEnvelope(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);
|
static bool IsDragZooming(int zoomStart, int zoomEnd);
|
||||||
virtual bool IsDragZooming() { return IsDragZooming(mZoomStart, mZoomEnd); }
|
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.
|
// Don't count right channels.
|
||||||
WaveTrack *NthAudioTrack(TrackList &list, int nn)
|
WaveTrack *NthAudioTrack(TrackList &list, int nn)
|
||||||
{
|
{
|
||||||
@ -117,6 +237,177 @@ namespace {
|
|||||||
}
|
}
|
||||||
return -1;
|
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
|
UIHandle::Result TimeShiftHandle::Click
|
||||||
@ -169,7 +460,7 @@ UIHandle::Result TimeShiftHandle::Click
|
|||||||
return Cancelled;
|
return Cancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackPanel::CreateListOfCapturedClips
|
CreateListOfCapturedClips
|
||||||
( mClipMoveState, viewInfo, *pTrack, *trackList,
|
( mClipMoveState, viewInfo, *pTrack, *trackList,
|
||||||
pProject->IsSyncLocked(), clickTime );
|
pProject->IsSyncLocked(), clickTime );
|
||||||
}
|
}
|
||||||
@ -459,7 +750,7 @@ UIHandle::Result TimeShiftHandle::Drag
|
|||||||
|
|
||||||
mClipMoveState.hSlideAmount = desiredSlideAmount;
|
mClipMoveState.hSlideAmount = desiredSlideAmount;
|
||||||
|
|
||||||
TrackPanel::DoSlideHorizontal( mClipMoveState, *trackList, *mCapturedTrack );
|
DoSlideHorizontal( mClipMoveState, *trackList, *mCapturedTrack );
|
||||||
|
|
||||||
if (mClipMoveState.capturedClipIsSelection) {
|
if (mClipMoveState.capturedClipIsSelection) {
|
||||||
// Slide the selection, too
|
// Slide the selection, too
|
||||||
|
@ -18,11 +18,30 @@ Paul Licameli
|
|||||||
#include "../../Snap.h"
|
#include "../../Snap.h"
|
||||||
#include "../../Track.h"
|
#include "../../Track.h"
|
||||||
|
|
||||||
#include "../../TrackPanel.h" // for ClipMoveState
|
|
||||||
|
|
||||||
struct HitTestResult;
|
struct HitTestResult;
|
||||||
class WaveClip;
|
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
|
class TimeShiftHandle final : public UIHandle
|
||||||
{
|
{
|
||||||
TimeShiftHandle();
|
TimeShiftHandle();
|
||||||
@ -33,6 +52,15 @@ class TimeShiftHandle final : public UIHandle
|
|||||||
(const AudacityProject *pProject, bool unsafe);
|
(const AudacityProject *pProject, bool unsafe);
|
||||||
|
|
||||||
public:
|
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 HitAnywhere(const AudacityProject *pProject);
|
||||||
static HitTestResult HitTest
|
static HitTestResult HitTest
|
||||||
(const wxMouseEvent &event, const wxRect &rect, const AudacityProject *pProject);
|
(const wxMouseEvent &event, const wxRect &rect, const AudacityProject *pProject);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user