1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-16 16:10:06 +02:00

Fix lots of snapping problems by moving snap-to-time into SnapManager.

Do a 'make dep' after updating, some #includes are added.
This commit is contained in:
BusinessmanProgrammerSteve 2010-02-02 19:43:52 +00:00
parent 8bc7f68edc
commit dbc4aab314
8 changed files with 209 additions and 55 deletions

View File

@ -4140,10 +4140,6 @@ void AudacityProject::TP_DisplaySelection()
} }
GetSelectionBar()->SetTimes(mViewInfo.sel0, mViewInfo.sel1, audioTime); GetSelectionBar()->SetTimes(mViewInfo.sel0, mViewInfo.sel1, audioTime);
if( mSnapTo ) {
mViewInfo.sel0 = GetSelectionBar()->GetLeftTime();
mViewInfo.sel1 = GetSelectionBar()->GetRightTime();
}
if (!gAudioIO->IsBusy() && !mLockPlayRegion) if (!gAudioIO->IsBusy() && !mLockPlayRegion)
mRuler->SetPlayRegion(mViewInfo.sel0, mViewInfo.sel1); mRuler->SetPlayRegion(mViewInfo.sel0, mViewInfo.sel1);

View File

@ -11,9 +11,12 @@
#include <math.h> #include <math.h>
#include "LabelTrack.h" #include "LabelTrack.h"
#include "Prefs.h"
#include "Project.h"
#include "Snap.h" #include "Snap.h"
#include "TrackPanel.h" #include "TrackPanel.h"
#include "WaveTrack.h" #include "WaveTrack.h"
#include "widgets/TimeTextCtrl.h"
int CompareSnapPoints(SnapPoint *s1, SnapPoint *s2) int CompareSnapPoints(SnapPoint *s1, SnapPoint *s2)
{ {
@ -21,10 +24,27 @@ int CompareSnapPoints(SnapPoint *s1, SnapPoint *s2)
} }
SnapManager::SnapManager(TrackList *tracks, TrackClipArray *exclusions, SnapManager::SnapManager(TrackList *tracks, TrackClipArray *exclusions,
double zoom, int pixelTolerance) double zoom, int pixelTolerance, bool noTimeSnap)
{ {
int i; int i;
// Grab time-snapping prefs (unless otherwise requested)
mSnapToTime = false;
TimeTextCtrl *ttc = NULL;
if (gPrefs->Read(wxT("/SnapTo"), 0L) != 0L && !noTimeSnap)
{
// Look up the format string
AudacityProject *p = GetActiveProject();
if (p) {
mSnapToTime = true;
ttc = new TimeTextCtrl(p, wxID_ANY, wxT(""), 0.0, p->GetRate());
wxString formatName;
gPrefs->Read(wxT("/SelectionFormat"), &formatName);
mFormat = ttc->GetBuiltinFormat(formatName);
ttc->SetFormatString(mFormat);
}
}
mSnapPoints = new SnapPointArray(CompareSnapPoints); mSnapPoints = new SnapPointArray(CompareSnapPoints);
if (zoom > 0 && pixelTolerance > 0) if (zoom > 0 && pixelTolerance > 0)
mTolerance = pixelTolerance / zoom; mTolerance = pixelTolerance / zoom;
@ -46,9 +66,10 @@ SnapManager::SnapManager(TrackList *tracks, TrackClipArray *exclusions,
LabelTrack *labelTrack = (LabelTrack *)track; LabelTrack *labelTrack = (LabelTrack *)track;
for(i = 0; i < labelTrack->GetNumLabels(); i++) { for(i = 0; i < labelTrack->GetNumLabels(); i++) {
const LabelStruct *label = labelTrack->GetLabel(i); const LabelStruct *label = labelTrack->GetLabel(i);
mSnapPoints->Add(new SnapPoint(label->t, labelTrack)); CondListAdd(label->t, labelTrack, ttc);
if (label->t1 != label->t) if (label->t1 != label->t) {
mSnapPoints->Add(new SnapPoint(label->t1, labelTrack)); CondListAdd(label->t1, labelTrack, ttc);
}
} }
} }
if (track->GetKind() == Track::Wave) { if (track->GetKind() == Track::Wave) {
@ -66,13 +87,26 @@ SnapManager::SnapManager(TrackList *tracks, TrackClipArray *exclusions,
if (skip) if (skip)
continue; continue;
} }
mSnapPoints->Add(new SnapPoint(clip->GetStartTime(), waveTrack)); CondListAdd(clip->GetStartTime(), waveTrack, ttc);
mSnapPoints->Add(new SnapPoint(clip->GetEndTime(), waveTrack)); CondListAdd(clip->GetEndTime(), waveTrack, ttc);
} }
} }
track = iter.Next(); track = iter.Next();
} }
if (ttc)
delete ttc;
}
// Adds to mSnapPoints, filtering by ttc if it's not NULL
void SnapManager::CondListAdd(double t, Track *tr, TimeTextCtrl *ttc)
{
if (ttc)
ttc->SetTimeValue(t);
if (!ttc || ttc->GetTimeValue() == t)
mSnapPoints->Add(new SnapPoint(t, tr));
} }
SnapManager::~SnapManager() SnapManager::~SnapManager()
@ -130,10 +164,11 @@ int SnapManager::Find(double t)
return index; return index;
} }
bool SnapManager::Snap(Track *currentTrack, // Helper: performs snap-to-points for Snap(). Returns true if a snap happened.
double t, bool SnapManager::SnapToPoints(Track *currentTrack,
bool rightEdge, double t,
double *out_t) bool rightEdge,
double *out_t)
{ {
int len = (int)mSnapPoints->GetCount(); int len = (int)mSnapPoints->GetCount();
*out_t = t; *out_t = t;
@ -193,6 +228,39 @@ bool SnapManager::Snap(Track *currentTrack,
return false; return false;
} }
bool SnapManager::Snap(Track *currentTrack,
double t,
bool rightEdge,
double *out_t,
bool *snappedPoint,
bool *snappedTime)
{
// First snap to points in mSnapPoints
*out_t = t;
*snappedPoint = SnapToPoints(currentTrack, t, rightEdge, out_t);
// Now snap to the time grid
*snappedTime = false;
if (mSnapToTime) {
if (*snappedPoint) {
// Since mSnapPoints only contains points on the grid, we're done
*snappedTime = true;
}
else {
// Snap time to the grid
AudacityProject *p = GetActiveProject();
TimeTextCtrl ttc(p, wxID_ANY, wxT(""), 0.0, p->GetRate());
ttc.SetFormatString(mFormat);
ttc.SetTimeValue(t);
*out_t = ttc.GetTimeValue();
*snappedTime = true;
}
}
return *snappedPoint || *snappedTime;
}
// Indentation settings for Vim and Emacs. // Indentation settings for Vim and Emacs.
// Please do not modify past this point. // Please do not modify past this point.
// //

View File

@ -22,6 +22,7 @@
#include "ViewInfo.h" #include "ViewInfo.h"
class TrackClipArray; class TrackClipArray;
class TimeTextCtrl;
class SnapPoint { class SnapPoint {
public: public:
@ -37,8 +38,10 @@ WX_DEFINE_SORTED_ARRAY(SnapPoint *, SnapPointArray);
class SnapManager { class SnapManager {
public: public:
// Set gridCtrl to a TimeTextCtrl to use for snap-to-time; if NULL we won't
// snap to time
SnapManager(TrackList *tracks, TrackClipArray *exclusions, SnapManager(TrackList *tracks, TrackClipArray *exclusions,
double zoom, int pixelTolerance); double zoom, int pixelTolerance, bool noTimeSnap = false);
~SnapManager(); ~SnapManager();
@ -49,18 +52,27 @@ class SnapManager {
bool Snap(Track *currentTrack, bool Snap(Track *currentTrack,
double t, double t,
bool rightEdge, bool rightEdge,
double *out_t); double *out_t,
bool *snappedPoint,
bool *snappedTime);
private: private:
void CondListAdd(double t, Track *tr, TimeTextCtrl *ttc);
double Get(int index); double Get(int index);
double Diff(double t, int index); double Diff(double t, int index);
int Find(double t, int i0, int i1); int Find(double t, int i0, int i1);
int Find(double t); int Find(double t);
bool SnapToPoints(Track *currentTrack, double t, bool rightEdge,
double *out_t);
double mEpsilon; double mEpsilon;
double mTolerance; double mTolerance;
double mZoom; double mZoom;
SnapPointArray *mSnapPoints; SnapPointArray *mSnapPoints;
// Info for snap-to-time
bool mSnapToTime;
wxString mFormat;
}; };
#endif #endif

View File

@ -215,6 +215,7 @@ is time to refresh some aspect of the screen.
#include "widgets/ASlider.h" #include "widgets/ASlider.h"
#include "widgets/Ruler.h" #include "widgets/Ruler.h"
#include "widgets/TimeTextCtrl.h"
#include <wx/arrimpl.cpp> #include <wx/arrimpl.cpp>
@ -443,8 +444,8 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id,
mBacking(NULL), mBacking(NULL),
mRefreshBacking(false), mRefreshBacking(false),
mAutoScrolling(false), mAutoScrolling(false),
vrulerSize(36,0), mVertScrollRemainder(0),
mVertScrollRemainder(0) vrulerSize(36,0)
#ifndef __WXGTK__ //Get rid if this pragma for gtk #ifndef __WXGTK__ //Get rid if this pragma for gtk
#pragma warning( default: 4355 ) #pragma warning( default: 4355 )
#endif #endif
@ -1791,13 +1792,9 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event,
if (mSnapManager) if (mSnapManager)
delete mSnapManager; delete mSnapManager;
// "/SnapTo" pref refers to snapping to time. Don't use the snap manager in mSnapManager = new SnapManager(mTracks, NULL,
// this case mViewInfo->zoom,
if (gPrefs->Read(wxT("/SnapTo"), 0L) == 0) { 4); // pixel tolerance
mSnapManager = new SnapManager(mTracks, NULL,
mViewInfo->zoom,
4); // pixel tolerance
}
mSnapLeft = -1; mSnapLeft = -1;
mSnapRight = -1; mSnapRight = -1;
@ -1938,8 +1935,11 @@ void TrackPanel::StartSelection(int mouseXCoordinate, int trackLeftEdge)
if (mSnapManager) { if (mSnapManager) {
mSnapLeft = -1; mSnapLeft = -1;
mSnapRight = -1; mSnapRight = -1;
if (mSnapManager->Snap(mCapturedTrack, mSelStart, false, &s)) { bool snappedPoint, snappedTime;
mSnapLeft = TimeToPosition(s, trackLeftEdge); if (mSnapManager->Snap(mCapturedTrack, mSelStart, false,
&s, &snappedPoint, &snappedTime)) {
if (snappedPoint)
mSnapLeft = TimeToPosition(s, trackLeftEdge);
} }
} }
@ -1978,15 +1978,22 @@ void TrackPanel::ExtendSelection(int mouseXCoordinate, int trackLeftEdge,
if (mSnapManager) { if (mSnapManager) {
mSnapLeft = -1; mSnapLeft = -1;
mSnapRight = -1; mSnapRight = -1;
if (mSnapManager->Snap(mCapturedTrack, sel0, false, &sel0)) { bool snappedPoint, snappedTime;
mSnapLeft = TimeToPosition(sel0, trackLeftEdge); if (mSnapManager->Snap(mCapturedTrack, sel0, false,
&sel0, &snappedPoint, &snappedTime)) {
if (snappedPoint)
mSnapLeft = TimeToPosition(sel0, trackLeftEdge);
} }
if (mSnapManager->Snap(mCapturedTrack, sel1, true, &sel1)) { if (mSnapManager->Snap(mCapturedTrack, sel1, true,
mSnapRight = TimeToPosition(sel1, trackLeftEdge); &sel1, &snappedPoint, &snappedTime)) {
if (snappedPoint)
mSnapRight = TimeToPosition(sel1, trackLeftEdge);
} }
if (mSnapLeft >= 0 && mSnapRight >= 0 && mSnapRight - mSnapLeft < 3) { // Check if selection endpoints are too close together to snap (unless
// Too close together. Better not to snap at all. // using snap-to-time -- then we always accept the snap results)
if (mSnapLeft >= 0 && mSnapRight >= 0 && mSnapRight - mSnapLeft < 3 &&
!snappedTime) {
sel0 = origSel0; sel0 = origSel0;
sel1 = origSel1; sel1 = origSel1;
mSnapLeft = -1; mSnapLeft = -1;
@ -2415,7 +2422,8 @@ void TrackPanel::StartSlide(wxMouseEvent & event)
mSnapManager = new SnapManager(mTracks, mSnapManager = new SnapManager(mTracks,
&mCapturedClipArray, &mCapturedClipArray,
mViewInfo->zoom, mViewInfo->zoom,
4); // pixel tolerance 4, // pixel tolerance
true); // don't snap to time
mSnapLeft = -1; mSnapLeft = -1;
mSnapRight = -1; mSnapRight = -1;
mSnapPreferRightEdge = false; mSnapPreferRightEdge = false;
@ -2510,8 +2518,11 @@ void TrackPanel::DoSlide(wxMouseEvent & event)
double newClipLeft = clipLeft; double newClipLeft = clipLeft;
double newClipRight = clipRight; double newClipRight = clipRight;
mSnapManager->Snap(mCapturedTrack, clipLeft, false, &newClipLeft); bool dummy1, dummy2;
mSnapManager->Snap(mCapturedTrack, clipRight, false, &newClipRight); mSnapManager->Snap(mCapturedTrack, clipLeft, false, &newClipLeft,
&dummy1, &dummy2);
mSnapManager->Snap(mCapturedTrack, clipRight, false, &newClipRight,
&dummy1, &dummy2);
// Only one of them is allowed to snap // Only one of them is allowed to snap
if (newClipLeft != clipLeft && newClipRight != clipRight) { if (newClipLeft != clipLeft && newClipRight != clipRight) {
@ -5518,11 +5529,18 @@ void TrackPanel::OnCursorLeft( bool shift, bool ctrl )
// Get currently focused track if there is one // Get currently focused track if there is one
t = GetFocusedTrack(); t = GetFocusedTrack();
bool snapToTime = (gPrefs->Read(wxT("/SnapTo"), 0L) != 0);
// Contract selection from the right to the left // Contract selection from the right to the left
if( shift && ctrl ) if( shift && ctrl )
{ {
// Reduce and constrain (counter-intuitive) // Reduce and constrain (counter-intuitive)
mViewInfo->sel1 -= multiplier / mViewInfo->zoom; if (snapToTime) {
mViewInfo->sel1 = GridMove(mViewInfo->sel1, -multiplier);
}
else {
mViewInfo->sel1 -= multiplier / mViewInfo->zoom;
}
if( mViewInfo->sel1 < mViewInfo->sel0 ) if( mViewInfo->sel1 < mViewInfo->sel0 )
{ {
mViewInfo->sel1 = mViewInfo->sel0; mViewInfo->sel1 = mViewInfo->sel0;
@ -5546,7 +5564,12 @@ void TrackPanel::OnCursorLeft( bool shift, bool ctrl )
} }
// Expand and constrain // Expand and constrain
mViewInfo->sel0 -= multiplier / mViewInfo->zoom; if (snapToTime) {
mViewInfo->sel0 = GridMove(mViewInfo->sel0, -multiplier);
}
else {
mViewInfo->sel0 -= multiplier / mViewInfo->zoom;
}
if( mViewInfo->sel0 < 0.0 ) if( mViewInfo->sel0 < 0.0 )
{ {
mViewInfo->sel0 = 0.0; mViewInfo->sel0 = 0.0;
@ -5573,7 +5596,12 @@ void TrackPanel::OnCursorLeft( bool shift, bool ctrl )
if( mViewInfo->sel0 == mViewInfo->sel1 ) if( mViewInfo->sel0 == mViewInfo->sel1 )
{ {
// Move and constrain // Move and constrain
mViewInfo->sel0 -= multiplier / mViewInfo->zoom; if (snapToTime) {
mViewInfo->sel0 = GridMove(mViewInfo->sel0, -multiplier);
}
else {
mViewInfo->sel0 -= multiplier / mViewInfo->zoom;
}
if( mViewInfo->sel0 < 0.0 ) if( mViewInfo->sel0 < 0.0 )
{ {
mViewInfo->sel0 = 0.0; mViewInfo->sel0 = 0.0;
@ -5616,11 +5644,18 @@ void TrackPanel::OnCursorRight( bool shift, bool ctrl )
// Get currently focused track if there is one // Get currently focused track if there is one
t = GetFocusedTrack(); t = GetFocusedTrack();
bool snapToTime = (gPrefs->Read(wxT("/SnapTo"), 0L) != 0);
// Contract selection from the left to the right // Contract selection from the left to the right
if( shift && ctrl ) if( shift && ctrl )
{ {
// Reduce and constrain (counter-intuitive) // Reduce and constrain (counter-intuitive)
mViewInfo->sel0 += multiplier / mViewInfo->zoom; if (snapToTime) {
mViewInfo->sel0 = GridMove(mViewInfo->sel0, multiplier);
}
else {
mViewInfo->sel0 += multiplier / mViewInfo->zoom;
}
if( mViewInfo->sel0 > mViewInfo->sel1 ) if( mViewInfo->sel0 > mViewInfo->sel1 )
{ {
mViewInfo->sel0 = mViewInfo->sel1; mViewInfo->sel0 = mViewInfo->sel1;
@ -5644,7 +5679,12 @@ void TrackPanel::OnCursorRight( bool shift, bool ctrl )
} }
// Expand and constrain // Expand and constrain
mViewInfo->sel1 += multiplier/mViewInfo->zoom; if (snapToTime) {
mViewInfo->sel1 = GridMove(mViewInfo->sel1, multiplier);
}
else {
mViewInfo->sel1 += multiplier/mViewInfo->zoom;
}
double end = mTracks->GetEndTime(); double end = mTracks->GetEndTime();
if( mViewInfo->sel1 > end ) if( mViewInfo->sel1 > end )
{ {
@ -5672,7 +5712,12 @@ void TrackPanel::OnCursorRight( bool shift, bool ctrl )
if (mViewInfo->sel0 == mViewInfo->sel1) if (mViewInfo->sel0 == mViewInfo->sel1)
{ {
// Move and constrain // Move and constrain
mViewInfo->sel1 += multiplier / mViewInfo->zoom; if (snapToTime) {
mViewInfo->sel1 = GridMove(mViewInfo->sel1, multiplier);
}
else {
mViewInfo->sel1 += multiplier / mViewInfo->zoom;
}
double end = mTracks->GetEndTime(); double end = mTracks->GetEndTime();
if( mViewInfo->sel1 > end ) if( mViewInfo->sel1 > end )
{ {
@ -5699,6 +5744,34 @@ void TrackPanel::OnCursorRight( bool shift, bool ctrl )
MakeParentModifyState(); MakeParentModifyState();
} }
// Handles moving a selection edge with the keyboard in snap-to-time mode;
// returns the moved value.
// Will move at least minPix pixels -- set minPix positive to move forward,
// negative to move backward.
double TrackPanel::GridMove(double t, int minPix)
{
TimeTextCtrl ttc(this, wxID_ANY, wxT(""), 0.0, GetProject()->GetRate());
wxString formatName;
gPrefs->Read(wxT("/SelectionFormat"), &formatName);
ttc.SetFormatString(ttc.GetBuiltinFormat(formatName));
ttc.SetTimeValue(t);
// Try incrementing/decrementing the value; if we've moved far enough we're
// done
double result;
minPix >= 0 ? ttc.Increment() : ttc.Decrement();
result = ttc.GetTimeValue();
if (fabs(result - t) * mViewInfo->zoom >= fabs(minPix)) {
return result;
}
// Otherwise, move minPix pixels, then snap to the time.
result = t + minPix / mViewInfo->zoom;
ttc.SetTimeValue(result);
result = ttc.GetTimeValue();
return result;
}
void TrackPanel::OnBoundaryMove(bool left, bool boundaryContract) void TrackPanel::OnBoundaryMove(bool left, bool boundaryContract)
{ {
// Move the left/right selection boundary, to either expand or contract the selection // Move the left/right selection boundary, to either expand or contract the selection

View File

@ -254,6 +254,8 @@ class TrackPanel:public wxPanel {
void SelectTracksByLabel( LabelTrack *t ); void SelectTracksByLabel( LabelTrack *t );
void SelectTrackLength(Track *t); void SelectTrackLength(Track *t);
// Helper for moving by keyboard with snap-to-grid enabled
double GridMove(double t, int minPix);
// AS: Cursor handling // AS: Cursor handling
bool SetCursorByActivity( ); bool SetCursorByActivity( );

View File

@ -106,25 +106,14 @@ void SelectionBar::Populate()
wxFlexGridSizer *mainSizer; wxFlexGridSizer *mainSizer;
wxBoxSizer *hSizer; wxBoxSizer *hSizer;
int formatIndex = 1;
/* we don't actually need a control yet, but we want to use it's methods /* we don't actually need a control yet, but we want to use it's methods
* to do some look-ups, so we'll have to create one. We can't make the * to do some look-ups, so we'll have to create one. We can't make the
* look-ups static because they depend on translations which are done at * look-ups static because they depend on translations which are done at
* runtime */ * runtime */
TimeTextCtrl *ttc = new TimeTextCtrl(this, wxID_ANY, wxT(""), 0.0, mRate); TimeTextCtrl *ttc = new TimeTextCtrl(this, wxID_ANY, wxT(""), 0.0, mRate);
wxString formatName; wxString formatName;
if (gPrefs->Read(wxT("/SelectionFormat"), &formatName)) { gPrefs->Read(wxT("/SelectionFormat"), &formatName);
for(int i = 0; i < ttc->GetNumBuiltins(); i++) { wxString format = ttc->GetBuiltinFormat(formatName);
if (ttc->GetBuiltinName(i) == formatName) {
formatIndex = i;
break;
}
}
}
else
formatIndex = 1;
wxString format = ttc->GetBuiltinFormat(formatIndex);
delete ttc; delete ttc;
mainSizer = new wxFlexGridSizer(7, 1, 1); mainSizer = new wxFlexGridSizer(7, 1, 1);

View File

@ -469,6 +469,18 @@ void TimeTextCtrl::SetTimeValue(double newTime)
ControlsToValue(); ControlsToValue();
} }
void TimeTextCtrl::Increment()
{
mFocusedDigit = mDigits.GetCount() - 1;
Increase(1);
}
void TimeTextCtrl::Decrement()
{
mFocusedDigit = mDigits.GetCount() - 1;
Decrease(1);
}
void TimeTextCtrl::EnableMenu(bool enable) void TimeTextCtrl::EnableMenu(bool enable)
{ {
#if wxUSE_TOOLTIPS #if wxUSE_TOOLTIPS

View File

@ -67,6 +67,8 @@ class TimeTextCtrl: public wxControl{
void SetFormatString(wxString formatString); void SetFormatString(wxString formatString);
void SetSampleRate(double sampleRate); void SetSampleRate(double sampleRate);
void SetTimeValue(double newTime); void SetTimeValue(double newTime);
void Increment();
void Decrement();
const double GetTimeValue(); const double GetTimeValue();
wxString GetTimeString(); wxString GetTimeString();