TrackPanel no longer implements the envelope tool...

... also implement ESC key for it
This commit is contained in:
Paul Licameli 2015-07-09 08:38:57 -04:00 committed by Paul Licameli
parent ef38af71dd
commit 2496b0d7bc
12 changed files with 400 additions and 273 deletions

View File

@ -1206,6 +1206,7 @@
28FE4A090ABF4E960056F5C4 /* sse_optimized.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28FE4A070ABF4E960056F5C4 /* sse_optimized.cpp */; };
5E000A211EC7B5D500E8FD93 /* SampleHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E000A1F1EC7B5D500E8FD93 /* SampleHandle.cpp */; };
5E7396441DAFD8C600BA0A4D /* TimeShiftHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E7396421DAFD8C600BA0A4D /* TimeShiftHandle.cpp */; };
5E7396471DAFD8F200BA0A4D /* EnvelopeHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E7396451DAFD8F200BA0A4D /* EnvelopeHandle.cpp */; };
5E02BFF21D1164DF00EB7578 /* Distortion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E02BFF01D1164DF00EB7578 /* Distortion.cpp */; };
5E07842E1DEE6B8600CA76EA /* FileException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E07842C1DEE6B8600CA76EA /* FileException.cpp */; };
5E0784311DF1E4F400CA76EA /* UserException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E07842F1DF1E4F400CA76EA /* UserException.cpp */; };
@ -3007,6 +3008,8 @@
5E000A201EC7B5D500E8FD93 /* SampleHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SampleHandle.h; sourceTree = "<group>"; };
5E7396421DAFD8C600BA0A4D /* TimeShiftHandle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimeShiftHandle.cpp; sourceTree = "<group>"; };
5E7396431DAFD8C600BA0A4D /* TimeShiftHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimeShiftHandle.h; sourceTree = "<group>"; };
5E7396451DAFD8F200BA0A4D /* EnvelopeHandle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EnvelopeHandle.cpp; sourceTree = "<group>"; };
5E7396461DAFD8F200BA0A4D /* EnvelopeHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EnvelopeHandle.h; sourceTree = "<group>"; };
5E02BFF01D1164DF00EB7578 /* Distortion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Distortion.cpp; sourceTree = "<group>"; };
5E02BFF11D1164DF00EB7578 /* Distortion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Distortion.h; sourceTree = "<group>"; };
5E07842C1DEE6B8600CA76EA /* FileException.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileException.cpp; sourceTree = "<group>"; };
@ -5733,6 +5736,7 @@
children = (
5E1512661DB0010C00702E29 /* CommonTrackPanelCell.cpp */,
5E74D2DD1CC4429700D88B0B /* EditCursorOverlay.cpp */,
5E7396451DAFD8F200BA0A4D /* EnvelopeHandle.cpp */,
5E74D2DF1CC4429700D88B0B /* PlayIndicatorOverlay.cpp */,
5E74D2E11CC4429700D88B0B /* Scrubbing.cpp */,
5E7396421DAFD8C600BA0A4D /* TimeShiftHandle.cpp */,
@ -5742,6 +5746,7 @@
5E73963C1DAFD86000BA0A4D /* ZoomHandle.cpp */,
5E1512671DB0010C00702E29 /* CommonTrackPanelCell.h */,
5E74D2DE1CC4429700D88B0B /* EditCursorOverlay.h */,
5E7396461DAFD8F200BA0A4D /* EnvelopeHandle.h */,
5E74D2E01CC4429700D88B0B /* PlayIndicatorOverlay.h */,
5E74D2E21CC4429700D88B0B /* Scrubbing.h */,
5E7396431DAFD8C600BA0A4D /* TimeShiftHandle.h */,
@ -7877,6 +7882,7 @@
280A8B4719F4403B0091DE70 /* ModuleManager.cpp in Sources */,
5EA018291EC7B226001F2996 /* NoteTrackUI.cpp in Sources */,
280A8B4A19F440880091DE70 /* EffectRack.cpp in Sources */,
5E7396471DAFD8F200BA0A4D /* EnvelopeHandle.cpp in Sources */,
28001B3E1A0F0E5D007DD161 /* NumericTextCtrl.cpp in Sources */,
28001B4B1A0F0EB6007DD161 /* SpectralSelectionBar.cpp in Sources */,
28BB98051A15BE6800D1CC80 /* NoiseReduction.cpp in Sources */,

View File

@ -575,6 +575,8 @@ audacity_SOURCES = \
tracks/ui/CommonTrackPanelCell.h \
tracks/ui/EditCursorOverlay.cpp \
tracks/ui/EditCursorOverlay.h \
tracks/ui/EnvelopeHandle.cpp \
tracks/ui/EnvelopeHandle.h \
tracks/ui/PlayIndicatorOverlay.cpp \
tracks/ui/PlayIndicatorOverlay.h \
tracks/ui/Scrubbing.cpp \

View File

@ -170,7 +170,6 @@ is time to refresh some aspect of the screen.
#include "AColor.h"
#include "AllThemeResources.h"
#include "AudioIO.h"
#include "Envelope.h"
#include "float_cast.h"
#include "LabelTrack.h"
#include "MixerBoard.h"
@ -1150,7 +1149,6 @@ void TrackPanel::HandleInterruptedDrag()
IsClosing,
IsAdjustingLabel,
IsRearranging,
IsEnveloping,
IsGainSliding,
IsPanSliding,
WasOverCutLine,
@ -1406,9 +1404,6 @@ bool TrackPanel::SetCursorByActivity( )
case IsSelecting:
SetCursor(*mSelectCursor);
return true;
case IsEnveloping:
SetCursor( unsafe ? *mDisabledCursor : *mEnvelopeCursor);
return true;
case IsRearranging:
SetCursor( unsafe ? *mDisabledCursor : *mRearrangeCursor);
return true;
@ -1750,16 +1745,11 @@ void TrackPanel::SetCursorAndTipWhenSelectTool( Track * t,
void TrackPanel::SetCursorAndTipByTool( int tool,
const wxMouseEvent &, wxString& )
{
bool unsafe = IsUnsafe();
// Change the cursor based on the active tool.
switch (tool) {
case selectTool:
wxFAIL;// should have already been handled
break;
case envelopeTool:
SetCursor(unsafe ? *mDisabledCursor : *mEnvelopeCursor);
break;
}
// doesn't actually change the tip itself, but it could (should?) do at some
// future date.
@ -3138,171 +3128,6 @@ bool mayDragWidth, bool onlyWithinSnapDistance,
}
}
/// HandleEnvelope gets called when the user is changing the
/// amplitude envelope on a track.
void TrackPanel::HandleEnvelope(wxMouseEvent & event)
{
if (event.LeftDown()) {
const auto foundCell = FindCell(event.m_x, event.m_y);
auto &pTrack = foundCell.pTrack;
auto &rect = foundCell.rect;
if (!pTrack || foundCell.type != CellType::Track)
return;
SetCapturedTrack(pTrack, IsEnveloping);
if (mCapturedTrack->GetKind() == Track::Wave)
{
mCapturedEnvelope =
((WaveTrack*)mCapturedTrack)->GetEnvelopeAtX(event.GetX());
} else {
mCapturedEnvelope = NULL;
}
mCapturedRect = rect;
mCapturedRect.y += kTopMargin;
mCapturedRect.height -= kTopMargin + kBottomMargin;
}
// AS: if there's actually a selected track, then forward all of the
// mouse events to its envelope.
if (mCapturedTrack)
ForwardEventToEnvelope(event);
// We test for IsEnveloping, because we could have had our action stopped already,
// and already recorded and the second mouse up is bogus.
// e.g could be stopped by some key press. Bug 1496.
if ((mMouseCapture == IsEnveloping ) && event.LeftUp()) {
SetCapturedTrack( NULL );
MakeParentPushState(
/* i18n-hint: (verb) Audacity has just adjusted the envelope .*/
_("Adjusted envelope."),
/* i18n-hint: The envelope is a curve that controls the audio loudness.*/
_("Envelope")
);
}
}
/// We've established we're a time track.
/// send events for its envelope.
void TrackPanel::ForwardEventToTimeTrackEnvelope(wxMouseEvent & event)
{
// Assume captured track was time
const auto ptimetrack = static_cast<TimeTrack *>(mCapturedTrack);
Envelope *pspeedenvelope = ptimetrack->GetEnvelope();
wxRect envRect = mCapturedRect;
double lower = ptimetrack->GetRangeLower(), upper = ptimetrack->GetRangeUpper();
const double dBRange = mViewInfo->dBr;
if (ptimetrack->GetDisplayLog()) {
// MB: silly way to undo the work of GetWaveYPos while still getting a logarithmic scale
lower = LINEAR_TO_DB(std::max(1.0e-7, lower)) / dBRange + 1.0;
upper = LINEAR_TO_DB(std::max(1.0e-7, upper)) / dBRange + 1.0;
}
if (event.ButtonDown()) {
mEnvelopeEditor = std::make_unique<EnvelopeEditor>(*pspeedenvelope, false);
mEnvelopeEditorRight.reset();
}
bool needUpdate =
mEnvelopeEditor &&
mEnvelopeEditor->MouseEvent(
event, envRect,
*mViewInfo,
ptimetrack->GetDisplayLog(), dBRange, lower, upper);
if (needUpdate) {
RefreshTrack(mCapturedTrack);
}
}
/// We've established we're a wave track.
/// send events for its envelope.
void TrackPanel::ForwardEventToWaveTrackEnvelope(wxMouseEvent & event)
{
// Assume captured track was wave
const auto pwavetrack = static_cast<WaveTrack*>(mCapturedTrack);
Envelope *penvelope = mCapturedEnvelope;
// Possibly no-envelope, for example when in spectrum view mode.
// if so, then bail out.
if (!penvelope)
return;
// AS: WaveTracks can be displayed in several different formats.
// This asks which one is in use. (ie, Wave, Spectrum, etc)
int display = pwavetrack->GetDisplay();
if (display == WaveTrack::Waveform) {
const bool dB = !pwavetrack->GetWaveformSettings().isLinear();
const double dBRange = pwavetrack->GetWaveformSettings().dBRange;
bool needUpdate;
// AS: Then forward our mouse event to the envelope.
// It'll recalculate and then tell us whether or not to redraw.
wxRect envRect = mCapturedRect;
float zoomMin, zoomMax;
pwavetrack->GetDisplayBounds(&zoomMin, &zoomMax);
if (event.ButtonDown()) {
mEnvelopeEditor = std::make_unique<EnvelopeEditor>(*penvelope, true);
mEnvelopeEditorRight.reset();
}
needUpdate =
mEnvelopeEditor &&
mEnvelopeEditor->MouseEvent(
event, envRect,
*mViewInfo,
dB, dBRange, zoomMin, zoomMax);
// If this track is linked to another track, make the identical
// change to the linked envelope:
// Assume linked track is wave or null
const auto link = static_cast<WaveTrack *>(mCapturedTrack->GetLink());
if (link) {
if (event.ButtonDown()) {
Envelope *e2 = link->GetEnvelopeAtX(event.GetX());
if (e2)
mEnvelopeEditorRight = std::make_unique<EnvelopeEditor>(*e2, true);
else {
// There isn't necessarily an envelope there; no guarantee a
// linked track has the same WaveClip structure...
}
}
if (mEnvelopeEditorRight) {
wxRect envRect = mCapturedRect;
float zoomMin, zoomMax;
pwavetrack->GetDisplayBounds(&zoomMin, &zoomMax);
needUpdate|= mEnvelopeEditorRight->MouseEvent(event, envRect,
*mViewInfo,
dB, dBRange,
zoomMin, zoomMax);
}
}
if (needUpdate) {
RefreshTrack(mCapturedTrack);
}
}
}
/// The Envelope class actually handles things at the mouse
/// event level, so we have to forward the events over. Envelope
/// will then tell us whether or not we need to redraw.
// AS: I'm not sure why we can't let the Envelope take care of
// redrawing itself. ?
void TrackPanel::ForwardEventToEnvelope(wxMouseEvent & event)
{
if (mCapturedTrack && mCapturedTrack->GetKind() == Track::Time)
{
ForwardEventToTimeTrackEnvelope( event );
}
else if (mCapturedTrack && mCapturedTrack->GetKind() == Track::Wave)
{
ForwardEventToWaveTrackEnvelope( event );
}
}
/// Determines if drag zooming is active
bool TrackPanel::IsDragZooming(int zoomStart, int zoomEnd)
{
@ -5700,8 +5525,6 @@ void TrackPanel::HandleTrackSpecificMouseEvent(wxMouseEvent & event)
auto &pCell = foundCell.pCell;
auto &rect = foundCell.rect;
bool unsafe = IsUnsafe();
//call HandleResize if I'm over the border area
// (Add margin back to bottom of the rectangle)
if (event.LeftDown() &&
@ -5804,18 +5627,13 @@ void TrackPanel::HandleTrackSpecificMouseEvent(wxMouseEvent & event)
case selectTool:
HandleSelect(event);
break;
case envelopeTool:
if (!unsafe)
HandleEnvelope(event);
break;
}
}
}
if ((event.Moving() || event.LeftUp()) &&
(mMouseCapture == IsUncaptured ))
// (mMouseCapture != IsSelecting ) &&
// (mMouseCapture != IsEnveloping)
// (mMouseCapture != IsSelecting )
{
HandleCursor(event);
}
@ -5857,9 +5675,7 @@ int TrackPanel::DetermineToolToUse( ToolsToolBar * pTtb, const wxMouseEvent & ev
int trackKind = pTrack->GetKind();
currentTool = selectTool; // the default.
if (trackKind == Track::Time){
currentTool = envelopeTool;
} else if( trackKind == Track::Label ){
if( trackKind == Track::Label ){
currentTool = selectTool;
} else if( trackKind != Track::Wave) {
currentTool = selectTool;
@ -5868,8 +5684,6 @@ int TrackPanel::DetermineToolToUse( ToolsToolBar * pTtb, const wxMouseEvent & ev
// From here on the order in which we hit test determines
// which tool takes priority in the rare cases where it
// could be more than one.
} else if( HitTestEnvelope( pTrack, rect, event ) ) {
currentTool = envelopeTool;
}
//Use the false argument since in multimode we don't
@ -5910,75 +5724,6 @@ auto TrackPanel::HitTestStretch
}
#endif
/// method that tells us if the mouse event landed on an
/// envelope boundary.
bool TrackPanel::HitTestEnvelope(Track *track, const wxRect &rect, const wxMouseEvent & event)
{
wxASSERT(track);
if( track->GetKind() != Track::Wave )
return false;
WaveTrack *wavetrack = (WaveTrack *)track;
Envelope *envelope = wavetrack->GetEnvelopeAtX(event.GetX());
if (!envelope)
return false;
const int displayType = wavetrack->GetDisplay();
// Not an envelope hit, unless we're using a type of wavetrack display
// suitable for envelopes operations, ie one of the Wave displays.
if ( displayType != WaveTrack::Waveform)
return false; // No envelope, not a hit, so return.
// Get envelope point, range 0.0 to 1.0
const bool dB = !wavetrack->GetWaveformSettings().isLinear();
const double envValue = envelope->GetValue(mViewInfo->PositionToTime(event.m_x, rect.x));
float zoomMin, zoomMax;
wavetrack->GetDisplayBounds(&zoomMin, &zoomMax);
const double dBRange = wavetrack->GetWaveformSettings().dBRange;
// Get y position of envelope point.
int yValue = GetWaveYPos( envValue,
zoomMin, zoomMax,
rect.height, dB, true, dBRange, false) + rect.y;
// Get y position of center line
int ctr = GetWaveYPos( 0.0,
zoomMin, zoomMax,
rect.height, dB, true, dBRange, false) + rect.y;
// Get y distance of mouse from center line (in pixels).
int yMouse = abs(ctr - event.m_y);
// Get y distance of envelope from center line (in pixels)
yValue = abs(ctr-yValue);
// JKC: It happens that the envelope is actually drawn offset from its
// 'true' position (it is 3 pixels wide). yMisalign is really a fudge
// factor to allow us to hit it exactly, but I wouldn't dream of
// calling it yFudgeFactor :)
const int yMisalign = 2;
// Perhaps yTolerance should be put into preferences?
const int yTolerance = 5; // how far from envelope we may be and count as a hit.
int distance;
// For amplification using the envelope we introduced the idea of contours.
// The contours have the same shape as the envelope, which may be partially off-screen.
// The contours are closer in to the center line.
int ContourSpacing = (int) (rect.height / (2* (zoomMax-zoomMin)));
const int MaxContours = 2;
// Adding ContourSpacing/2 selects a region either side of the contour.
int yDisplace = yValue - yMisalign - yMouse + ContourSpacing/2;
if (yDisplace > (MaxContours * ContourSpacing))
return false;
// Subtracting the ContourSpacing/2 we added earlier ensures distance is centred on the contour.
distance = abs( ( yDisplace % ContourSpacing ) - ContourSpacing/2);
return( distance < yTolerance );
}
double TrackPanel::GetMostRecentXPos()
{
return mViewInfo->PositionToTime(mMouseMostRecentX, GetLabelWidth());

View File

@ -28,7 +28,6 @@
class wxMenu;
class wxRect;
class EnvelopeEditor;
class LabelTrack;
class SpectrumAnalyst;
class Track;
@ -52,7 +51,6 @@ class ViewInfo;
class NoteTrack;
class WaveTrack;
class WaveClip;
class Envelope;
class UIHandle;
// Declared elsewhere, to reduce compilation dependencies
@ -375,7 +373,6 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
// Working out where to dispatch the event to.
virtual int DetermineToolToUse( ToolsToolBar * pTtb, const wxMouseEvent & event);
virtual bool HitTestEnvelope(Track *track, const wxRect &rect, const wxMouseEvent & event);
#ifdef USE_MIDI
// data for NoteTrack interactive stretch operations:
// Stretching applies to a selected region after quantizing the
@ -470,12 +467,6 @@ public:
protected:
virtual void MaySetOnDemandTip( Track * t, wxString &tip );
// AS: Envelope editing handlers
virtual void HandleEnvelope(wxMouseEvent & event);
virtual void ForwardEventToTimeTrackEnvelope(wxMouseEvent & event);
virtual void ForwardEventToWaveTrackEnvelope(wxMouseEvent & event);
virtual void ForwardEventToEnvelope(wxMouseEvent &event);
static bool IsDragZooming(int zoomStart, int zoomEnd);
virtual bool IsDragZooming() { return IsDragZooming(mZoomStart, mZoomEnd); }
@ -728,7 +719,6 @@ protected:
#endif
Track *mCapturedTrack;
Envelope *mCapturedEnvelope;
WaveTrackLocation mCapturedTrackLocation;
wxRect mCapturedTrackLocationRect;
wxRect mCapturedRect;
@ -821,7 +811,6 @@ public:
IsResizingBetweenLinkedTracks,
IsResizingBelowLinkedTracks,
IsRearranging,
IsEnveloping,
IsMuting,
IsSoloing,
IsGainSliding,
@ -892,9 +881,6 @@ protected:
// Keeps track of extra fractional vertical scroll steps
double mVertScrollRemainder;
std::unique_ptr<EnvelopeEditor> mEnvelopeEditor;
std::unique_ptr<EnvelopeEditor> mEnvelopeEditorRight;
protected:
// The screenshot class needs to access internals

View File

@ -87,6 +87,7 @@ ToolsToolBar::ToolsToolBar()
mMessageOfTool[selectTool] = _("Click and drag to select audio");
// TODO: this message isn't appropriate for time track
mMessageOfTool[envelopeTool] = _("Click and drag to edit the amplitude envelope");
// TODO: message should also mention the brush. Describing the modifier key

View File

@ -17,6 +17,7 @@ Paul Licameli split from TrackPanel.cpp
#include "../../../../TrackPanelMouseEvent.h"
#include "../../../../toolbars/ToolsToolBar.h"
#include "../../../ui/EnvelopeHandle.h"
#include "SampleHandle.h"
#include "../../../ui/TimeShiftHandle.h"
@ -34,6 +35,10 @@ HitTestResult WaveTrack::HitTest
int currentTool = -1;
if (event.event.CmdDown())
result = TimeShiftHandle::HitAnywhere(pProject);
else if (NULL !=
(result = EnvelopeHandle::WaveTrackHitTest(event.event, event.rect, pProject, this))
.preview.cursor)
;
else if (NULL != (result =
TimeShiftHandle::HitTest(event.event, event.rect, pProject)).preview.cursor)
;

View File

@ -13,12 +13,25 @@ Paul Licameli split from TrackPanel.cpp
#include "TimeTrackVRulerControls.h"
#include "../../../HitTestResult.h"
#include "../../../Project.h"
#include "../../../toolbars/ToolsToolBar.h"
#include "../../ui/EnvelopeHandle.h"
HitTestResult TimeTrack::HitTest
(const TrackPanelMouseEvent &event,
const AudacityProject *pProject)
{
return Track::HitTest(event, pProject);
HitTestResult result = Track::HitTest(event, pProject);
if (result.preview.cursor)
return result;
const ToolsToolBar *const pTtb = pProject->GetToolsToolBar();
if (pTtb->IsDown(multiTool))
// No hit test --unconditional availability.
result = EnvelopeHandle::HitAnywhere(pProject);
return result;
}
TrackControls *TimeTrack::GetControls()

View File

@ -0,0 +1,286 @@
/**********************************************************************
Audacity: A Digital Audio Editor
EnvelopeHandle.cpp
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#include "../../Audacity.h"
#include "EnvelopeHandle.h"
#include "../../MemoryX.h"
#include "../../Envelope.h"
#include "../../HitTestResult.h"
#include "../../prefs/WaveformSettings.h"
#include "../../Project.h"
#include "../../RefreshCode.h"
#include "../../toolbars/ToolsToolBar.h"
#include "../../TimeTrack.h"
#include "../../TrackArtist.h"
#include "../../TrackPanelMouseEvent.h"
#include "../../ViewInfo.h"
#include "../../WaveTrack.h"
#include "../../../images/Cursors.h"
EnvelopeHandle::EnvelopeHandle()
{
}
EnvelopeHandle &EnvelopeHandle::Instance()
{
static EnvelopeHandle instance;
return instance;
}
HitTestPreview EnvelopeHandle::HitPreview(const AudacityProject *pProject, bool unsafe)
{
static auto disabledCursor =
::MakeCursor(wxCURSOR_NO_ENTRY, DisabledCursorXpm, 16, 16);
static auto envelopeCursor =
::MakeCursor(wxCURSOR_ARROW, EnvCursorXpm, 16, 16);
const ToolsToolBar *const ttb = pProject->GetToolsToolBar();
return {
ttb->GetMessageForTool(envelopeTool),
(unsafe
? &*disabledCursor
: &*envelopeCursor)
};
}
HitTestResult EnvelopeHandle::HitAnywhere(const AudacityProject *pProject)
{
const bool unsafe = pProject->IsAudioActive();
return {
HitPreview(pProject, unsafe),
(unsafe
? NULL
: &Instance())
};
}
HitTestResult EnvelopeHandle::WaveTrackHitTest
(const wxMouseEvent &event, const wxRect &rect,
const AudacityProject *pProject, Cell *pCell)
{
const ViewInfo &viewInfo = pProject->GetViewInfo();
Track *const pTrack = static_cast<Track*>(pCell);
/// method that tells us if the mouse event landed on an
/// envelope boundary.
if (pTrack->GetKind() != Track::Wave)
return {};
WaveTrack *const wavetrack = static_cast<WaveTrack*>(pTrack);
Envelope *const envelope = wavetrack->GetEnvelopeAtX(event.GetX());
if (!envelope)
return {};
const int displayType = wavetrack->GetDisplay();
// Not an envelope hit, unless we're using a type of wavetrack display
// suitable for envelopes operations, ie one of the Wave displays.
if (displayType != WaveTrack::Waveform)
return {}; // No envelope, not a hit, so return.
// Get envelope point, range 0.0 to 1.0
const bool dB = !wavetrack->GetWaveformSettings().isLinear();
const double envValue =
envelope->GetValue(viewInfo.PositionToTime(event.m_x, rect.x));
float zoomMin, zoomMax;
wavetrack->GetDisplayBounds(&zoomMin, &zoomMax);
const float dBRange = wavetrack->GetWaveformSettings().dBRange;
// Get y position of envelope point.
int yValue = GetWaveYPos(envValue,
zoomMin, zoomMax,
rect.height, dB, true, dBRange, false) + rect.y;
// Get y position of center line
int ctr = GetWaveYPos(0.0,
zoomMin, zoomMax,
rect.height, dB, true, dBRange, false) + rect.y;
// Get y distance of mouse from center line (in pixels).
int yMouse = abs(ctr - event.m_y);
// Get y distance of envelope from center line (in pixels)
yValue = abs(ctr - yValue);
// JKC: It happens that the envelope is actually drawn offset from its
// 'true' position (it is 3 pixels wide). yMisalign is really a fudge
// factor to allow us to hit it exactly, but I wouldn't dream of
// calling it yFudgeFactor :)
const int yMisalign = 2;
// Perhaps yTolerance should be put into preferences?
const int yTolerance = 5; // how far from envelope we may be and count as a hit.
int distance;
// For amplification using the envelope we introduced the idea of contours.
// The contours have the same shape as the envelope, which may be partially off-screen.
// The contours are closer in to the center line.
int ContourSpacing = (int)(rect.height / (2 * (zoomMax - zoomMin)));
const int MaxContours = 2;
// Adding ContourSpacing/2 selects a region either side of the contour.
int yDisplace = yValue - yMisalign - yMouse + ContourSpacing / 2;
if (yDisplace > (MaxContours * ContourSpacing))
return {};
// Subtracting the ContourSpacing/2 we added earlier ensures distance is centred on the contour.
distance = abs((yDisplace % ContourSpacing) - ContourSpacing / 2);
if (distance >= yTolerance)
return {};
return HitAnywhere(pProject);
}
EnvelopeHandle::~EnvelopeHandle()
{
}
UIHandle::Result EnvelopeHandle::Click
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
const wxMouseEvent &event = evt.event;
const ViewInfo &viewInfo = pProject->GetViewInfo();
Track *const pTrack = static_cast<Track*>(evt.pCell);
using namespace RefreshCode;
const bool unsafe = pProject->IsAudioActive();
if (unsafe)
return Cancelled;
if (pTrack->GetKind() == Track::Wave) {
WaveTrack *const wt = static_cast<WaveTrack*>(pTrack);
if (wt->GetDisplay() != WaveTrack::Waveform)
return Cancelled;
auto clickedEnvelope =
wt->GetEnvelopeAtX(event.GetX());
if (!clickedEnvelope)
return Cancelled;
mLog = !wt->GetWaveformSettings().isLinear();
wt->GetDisplayBounds(&mLower, &mUpper);
mdBRange = wt->GetWaveformSettings().dBRange;
mEnvelopeEditor =
std::make_unique< EnvelopeEditor >( *clickedEnvelope, true );
mEnvelopeEditorRight.reset();
// Assume linked track is wave or null
auto partner = static_cast<WaveTrack*>(wt->GetLink());
if (partner)
{
clickedEnvelope = partner->GetEnvelopeAtX(event.GetX());
if (clickedEnvelope)
mEnvelopeEditorRight =
std::make_unique< EnvelopeEditor >( *clickedEnvelope, true );
}
}
else if (pTrack->GetKind() == Track::Time)
{
TimeTrack *const tt = static_cast<TimeTrack*>(pTrack);
auto clickedEnvelope = tt->GetEnvelope();
if (!clickedEnvelope)
return Cancelled;
mLog = tt->GetDisplayLog();
mLower = tt->GetRangeLower(), mUpper = tt->GetRangeUpper();
if (mLog) {
// MB: silly way to undo the work of GetWaveYPos while still getting a logarithmic scale
mdBRange = viewInfo.dBr;
mLower = LINEAR_TO_DB(std::max(1.0e-7, double(mLower))) / mdBRange + 1.0;
mUpper = LINEAR_TO_DB(std::max(1.0e-7, double(mUpper))) / mdBRange + 1.0;
}
mEnvelopeEditor =
std::make_unique< EnvelopeEditor >( *clickedEnvelope, false );
mEnvelopeEditorRight.reset();
}
else
return Cancelled;
mRect = evt.rect;
const bool needUpdate = ForwardEventToEnvelopes(event, viewInfo);
return needUpdate ? RefreshCell : RefreshNone;
}
UIHandle::Result EnvelopeHandle::Drag
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
using namespace RefreshCode;
const wxMouseEvent &event = evt.event;
const ViewInfo &viewInfo = pProject->GetViewInfo();
const bool unsafe = pProject->IsAudioActive();
if (unsafe) {
this->Cancel(pProject);
return RefreshCell | Cancelled;
}
const bool needUpdate = ForwardEventToEnvelopes(event, viewInfo);
return needUpdate ? RefreshCell : RefreshNone;
}
HitTestPreview EnvelopeHandle::Preview
(const TrackPanelMouseEvent &, const AudacityProject *pProject)
{
return HitPreview(pProject, false);
}
UIHandle::Result EnvelopeHandle::Release
(const TrackPanelMouseEvent &evt, AudacityProject *pProject,
wxWindow *)
{
const wxMouseEvent &event = evt.event;
const ViewInfo &viewInfo = pProject->GetViewInfo();
const bool unsafe = pProject->IsAudioActive();
if (unsafe)
return this->Cancel(pProject);
const bool needUpdate = ForwardEventToEnvelopes(event, viewInfo);
pProject->PushState(
/* i18n-hint: (verb) Audacity has just adjusted the envelope .*/
_("Adjusted envelope."),
/* i18n-hint: The envelope is a curve that controls the audio loudness.*/
_("Envelope")
);
mEnvelopeEditor.reset();
mEnvelopeEditorRight.reset();
using namespace RefreshCode;
return needUpdate ? RefreshCell : RefreshNone;
}
UIHandle::Result EnvelopeHandle::Cancel(AudacityProject *pProject)
{
pProject->RollbackState();
mEnvelopeEditor.reset();
mEnvelopeEditorRight.reset();
return RefreshCode::RefreshCell;
}
bool EnvelopeHandle::ForwardEventToEnvelopes
(const wxMouseEvent &event, const ViewInfo &viewInfo)
{
/// The Envelope class actually handles things at the mouse
/// event level, so we have to forward the events over. Envelope
/// will then tell us whether or not we need to redraw.
// AS: I'm not sure why we can't let the Envelope take care of
// redrawing itself. ?
bool needUpdate =
mEnvelopeEditor->MouseEvent(
event, mRect, viewInfo, mLog, mdBRange, mLower, mUpper);
if (mEnvelopeEditorRight)
needUpdate |=
mEnvelopeEditorRight->MouseEvent(
event, mRect, viewInfo, mLog, mdBRange, mLower, mUpper);
return needUpdate;
}

View File

@ -0,0 +1,72 @@
/**********************************************************************
Audacity: A Digital Audio Editor
EnvelopeHandle.h
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#ifndef __AUDACITY_ENVELOPE_HANDLE__
#define __AUDACITY_ENVELOPE_HANDLE__
#include "../../UIHandle.h"
#include "../../MemoryX.h"
class wxMouseEvent;
#include <wx/gdicmn.h>
class EnvelopeEditor;
struct HitTestResult;
class ViewInfo;
class WaveTrack;
class EnvelopeHandle final : public UIHandle
{
EnvelopeHandle();
EnvelopeHandle(const EnvelopeHandle&) = delete;
EnvelopeHandle &operator=(const EnvelopeHandle&) = delete;
static EnvelopeHandle& Instance();
static HitTestPreview HitPreview(const AudacityProject *pProject, bool unsafe);
public:
static HitTestResult HitAnywhere(const AudacityProject *pProject);
static HitTestResult WaveTrackHitTest
(const wxMouseEvent &event, const wxRect &rect,
const AudacityProject *pProject, Cell *pCell);
virtual ~EnvelopeHandle();
Result Click
(const TrackPanelMouseEvent &event, AudacityProject *pProject) override;
Result Drag
(const TrackPanelMouseEvent &event, AudacityProject *pProject) override;
HitTestPreview Preview
(const TrackPanelMouseEvent &event, const AudacityProject *pProject)
override;
Result Release
(const TrackPanelMouseEvent &event, AudacityProject *pProject,
wxWindow *pParent) override;
Result Cancel(AudacityProject *pProject) override;
bool StopsOnKeystroke() override { return true; }
private:
bool ForwardEventToEnvelopes
(const wxMouseEvent &event, const ViewInfo &viewInfo);
wxRect mRect{};
bool mLog{};
float mLower{}, mUpper{};
double mdBRange{};
std::unique_ptr<EnvelopeEditor> mEnvelopeEditor;
std::unique_ptr<EnvelopeEditor> mEnvelopeEditorRight;
};
#endif

View File

@ -17,6 +17,7 @@ Paul Licameli split from TrackPanel.cpp
#include "../../Project.h"
#include "../../toolbars/ToolsToolBar.h"
#include "EnvelopeHandle.h"
#include "../playabletrack/wavetrack/ui/SampleHandle.h"
#include "ZoomHandle.h"
#include "TimeShiftHandle.h"
@ -30,6 +31,9 @@ HitTestResult Track::HitTest
const bool isMultiTool = pTtb->IsDown(multiTool);
if (!isMultiTool) {
switch (pTtb->GetCurrentTool()) {
case envelopeTool:
// Pass "false" for unsafe -- let the tool decide to cancel itself
return EnvelopeHandle::HitAnywhere(pProject);
case drawTool:
return SampleHandle::HitAnywhere(event.event, pProject);
case zoomTool:
@ -38,7 +42,6 @@ HitTestResult Track::HitTest
return TimeShiftHandle::HitAnywhere(pProject);
case selectTool:
case envelopeTool:
default:
// cases not yet implemented
// fallthru

View File

@ -239,6 +239,7 @@
<ClCompile Include="..\..\..\src\tracks\timetrack\ui\TimeTrackVRulerControls.cpp" />
<ClCompile Include="..\..\..\src\tracks\ui\CommonTrackPanelCell.cpp" />
<ClCompile Include="..\..\..\src\tracks\ui\EditCursorOverlay.cpp" />
<ClCompile Include="..\..\..\src\tracks\ui\EnvelopeHandle.cpp" />
<ClCompile Include="..\..\..\src\tracks\ui\PlayIndicatorOverlay.cpp" />
<ClCompile Include="..\..\..\src\tracks\ui\Scrubbing.cpp" />
<ClCompile Include="..\..\..\src\tracks\ui\TimeShiftHandle.cpp" />
@ -500,6 +501,7 @@
<ClInclude Include="..\..\..\src\tracks\labeltrack\ui\LabelTrackVRulerControls.h" />
<ClInclude Include="..\..\..\src\tracks\timetrack\ui\TimeTrackControls.h" />
<ClInclude Include="..\..\..\src\tracks\ui\EditCursorOverlay.h" />
<ClInclude Include="..\..\..\src\tracks\ui\EnvelopeHandle.h" />
<ClInclude Include="..\..\..\src\tracks\ui\PlayIndicatorOverlay.h" />
<ClInclude Include="..\..\..\src\tracks\ui\Scrubbing.h" />
<ClInclude Include="..\..\..\src\TranslatableStringArray.h" />

View File

@ -956,6 +956,9 @@
<ClCompile Include="..\..\..\src\tracks\ui\TimeShiftHandle.cpp">
<Filter>src\tracks\ui</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\tracks\ui\EnvelopeHandle.cpp">
<Filter>src\tracks\ui</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\tracks\timetrack\ui\TimeTrackControls.cpp">
<Filter>src\tracks\timetrack\ui</Filter>
</ClCompile>
@ -1990,6 +1993,9 @@
<ClInclude Include="..\..\..\src\tracks\ui\TimeShiftHandle.h">
<Filter>src\tracks\ui</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\tracks\ui\EnvelopeHandle.h">
<Filter>src\tracks\ui</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="..\..\audacity.ico">