mirror of
https://github.com/cookiengineer/audacity
synced 2025-11-14 17:14:07 +01:00
TrackPanel no longer implements the envelope tool...
... also implement ESC key for it
This commit is contained in:
committed by
Paul Licameli
parent
ef38af71dd
commit
2496b0d7bc
@@ -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)
|
||||
;
|
||||
|
||||
@@ -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()
|
||||
|
||||
286
src/tracks/ui/EnvelopeHandle.cpp
Normal file
286
src/tracks/ui/EnvelopeHandle.cpp
Normal 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;
|
||||
}
|
||||
72
src/tracks/ui/EnvelopeHandle.h
Normal file
72
src/tracks/ui/EnvelopeHandle.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user