mirror of
https://github.com/cookiengineer/audacity
synced 2025-11-14 17:14:07 +01:00
TrackPanel no longer implements the draw tool...
... also implement ESC key for it
This commit is contained in:
committed by
Paul Licameli
parent
85c03bb3b3
commit
f1f254f974
483
src/tracks/playabletrack/wavetrack/ui/SampleHandle.cpp
Normal file
483
src/tracks/playabletrack/wavetrack/ui/SampleHandle.cpp
Normal file
@@ -0,0 +1,483 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
SampleHandle.cpp
|
||||
|
||||
Paul Licameli split from TrackPanel.cpp
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "../../../../Audacity.h"
|
||||
#include "SampleHandle.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include "../../../../MemoryX.h"
|
||||
#include <wx/gdicmn.h>
|
||||
|
||||
#include "../../../../Envelope.h"
|
||||
#include "../../../../HitTestResult.h"
|
||||
#include "../../../../prefs/WaveformSettings.h"
|
||||
#include "../../../../Project.h"
|
||||
#include "../../../../RefreshCode.h"
|
||||
#include "../../../../toolbars/ToolsToolBar.h"
|
||||
#include "../../../../TrackArtist.h"
|
||||
#include "../../../../TrackPanelMouseEvent.h"
|
||||
#include "../../../../UndoManager.h"
|
||||
#include "../../../../ViewInfo.h"
|
||||
#include "../../../../WaveTrack.h"
|
||||
#include "../../../../../images/Cursors.h"
|
||||
|
||||
|
||||
static const int SMOOTHING_KERNEL_RADIUS = 3;
|
||||
static const int SMOOTHING_BRUSH_RADIUS = 5;
|
||||
static const double SMOOTHING_PROPORTION_MAX = 0.7;
|
||||
static const double SMOOTHING_PROPORTION_MIN = 0.0;
|
||||
|
||||
SampleHandle::SampleHandle()
|
||||
{
|
||||
}
|
||||
|
||||
SampleHandle &SampleHandle::Instance()
|
||||
{
|
||||
static SampleHandle instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
HitTestPreview SampleHandle::HitPreview
|
||||
(const wxMouseEvent &event, const AudacityProject *pProject, bool unsafe)
|
||||
{
|
||||
static auto disabledCursor =
|
||||
::MakeCursor(wxCURSOR_NO_ENTRY, DisabledCursorXpm, 16, 16);
|
||||
static wxCursor smoothCursor{ wxCURSOR_SPRAYCAN };
|
||||
static auto pencilCursor =
|
||||
::MakeCursor(wxCURSOR_PENCIL, DrawCursorXpm, 12, 22);
|
||||
const ToolsToolBar *const ttb = pProject->GetToolsToolBar();
|
||||
return {
|
||||
ttb->GetMessageForTool(drawTool),
|
||||
(unsafe
|
||||
? &*disabledCursor
|
||||
: (event.AltDown()
|
||||
? &smoothCursor
|
||||
: &*pencilCursor))
|
||||
};
|
||||
}
|
||||
|
||||
HitTestResult SampleHandle::HitAnywhere
|
||||
(const wxMouseEvent &event, const AudacityProject *pProject)
|
||||
{
|
||||
const bool unsafe = pProject->IsAudioActive();
|
||||
return {
|
||||
HitPreview(event, pProject, unsafe),
|
||||
(unsafe
|
||||
? NULL
|
||||
: &Instance())
|
||||
};
|
||||
}
|
||||
|
||||
namespace {
|
||||
inline double adjustTime(const WaveTrack *wt, double time)
|
||||
{
|
||||
// Round to an exact sample time
|
||||
return wt->LongSamplesToTime(wt->TimeToLongSamples(time));
|
||||
}
|
||||
|
||||
// Is the sample horizontally nearest to the cursor sufficiently separated
|
||||
// from its neighbors that the pencil tool should be allowed to drag it?
|
||||
bool SampleResolutionTest
|
||||
( const ViewInfo &viewInfo, const WaveTrack *wt, double time, int width )
|
||||
{
|
||||
// Require more than 3 pixels per sample
|
||||
const wxInt64 xx = std::max(wxInt64(0), viewInfo.TimeToPosition(time));
|
||||
ZoomInfo::Intervals intervals;
|
||||
const double rate = wt->GetRate();
|
||||
viewInfo.FindIntervals(rate, intervals, width);
|
||||
ZoomInfo::Intervals::const_iterator it = intervals.begin(),
|
||||
end = intervals.end(), prev;
|
||||
wxASSERT(it != end && it->position == 0);
|
||||
do
|
||||
prev = it++;
|
||||
while (it != end && it->position <= xx);
|
||||
const double threshold = 3 * rate;
|
||||
// three times as many pixels per second, as samples
|
||||
return prev->averageZoom > threshold;
|
||||
}
|
||||
}
|
||||
|
||||
HitTestResult SampleHandle::HitTest
|
||||
(const wxMouseEvent &event, const wxRect &rect,
|
||||
const AudacityProject *pProject, Track *pTrack)
|
||||
{
|
||||
const ViewInfo &viewInfo = pProject->GetViewInfo();
|
||||
|
||||
/// method that tells us if the mouse event landed on an
|
||||
/// editable sample
|
||||
if (pTrack->GetKind() != Track::Wave)
|
||||
return {};
|
||||
|
||||
WaveTrack *wavetrack = static_cast<WaveTrack*>(pTrack);
|
||||
|
||||
const int displayType = wavetrack->GetDisplay();
|
||||
if (WaveTrack::Waveform != displayType)
|
||||
return {}; // Not a wave, so return.
|
||||
|
||||
const double tt =
|
||||
adjustTime(wavetrack, viewInfo.PositionToTime(event.m_x, rect.x));
|
||||
if (!SampleResolutionTest(viewInfo, wavetrack, tt, rect.width))
|
||||
return {};
|
||||
|
||||
// Just get one sample.
|
||||
float oneSample;
|
||||
const double rate = wavetrack->GetRate();
|
||||
const auto s0 = (sampleCount)(tt * rate + 0.5);
|
||||
if (! wavetrack->Get((samplePtr)&oneSample, floatSample, s0, 1, fillZero,
|
||||
// Do not propagate exception but return a failure value
|
||||
false) )
|
||||
return {};
|
||||
|
||||
// Get y distance of envelope point from center line (in pixels).
|
||||
float zoomMin, zoomMax;
|
||||
|
||||
wavetrack->GetDisplayBounds(&zoomMin, &zoomMax);
|
||||
|
||||
double envValue = 1.0;
|
||||
Envelope* env = wavetrack->GetEnvelopeAtX(event.GetX());
|
||||
if (env)
|
||||
// Calculate sample as it would be rendered, so quantize time
|
||||
envValue = env->GetValue( tt, 1.0 / wavetrack->GetRate() );
|
||||
|
||||
const bool dB = !wavetrack->GetWaveformSettings().isLinear();
|
||||
int yValue = GetWaveYPos(oneSample * envValue,
|
||||
zoomMin, zoomMax,
|
||||
rect.height, dB, true,
|
||||
wavetrack->GetWaveformSettings().dBRange, false) + rect.y;
|
||||
|
||||
// Get y position of mouse (in pixels)
|
||||
int yMouse = event.m_y;
|
||||
|
||||
// Perhaps yTolerance should be put into preferences?
|
||||
const int yTolerance = 10; // More tolerance on samples than on envelope.
|
||||
if (abs(yValue - yMouse) >= yTolerance)
|
||||
return {};
|
||||
|
||||
return HitAnywhere(event, pProject);
|
||||
}
|
||||
|
||||
SampleHandle::~SampleHandle()
|
||||
{
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// Determines if we can edit samples in a wave track.
|
||||
/// Also pops up warning messages in certain cases where we can't.
|
||||
/// @return true if we can edit the samples, false otherwise.
|
||||
bool IsSampleEditingPossible
|
||||
(const wxMouseEvent &event,
|
||||
const wxRect &rect, const ViewInfo &viewInfo, Track *pTrack, int width)
|
||||
{
|
||||
if (pTrack->GetKind() != Track::Wave)
|
||||
return false;
|
||||
WaveTrack *wt = static_cast<WaveTrack*>(pTrack);
|
||||
|
||||
//Get out of here if we shouldn't be drawing right now:
|
||||
//If we aren't displaying the waveform, Display a message dialog
|
||||
const int display = wt->GetDisplay();
|
||||
if (WaveTrack::Waveform != display)
|
||||
{
|
||||
wxMessageBox(_(
|
||||
"To use Draw, choose 'Waveform' or 'Waveform (dB)' in the Track Dropdown Menu."),
|
||||
_("Draw Tool"));
|
||||
return false;
|
||||
}
|
||||
|
||||
//If we aren't zoomed in far enough, show a message dialog.
|
||||
const double time = adjustTime(wt, viewInfo.PositionToTime(event.m_x, rect.x));
|
||||
if (!SampleResolutionTest(viewInfo, wt, time, width))
|
||||
{
|
||||
wxMessageBox(_(
|
||||
"To use Draw, zoom in further until you can see the individual samples."),
|
||||
_("Draw Tool"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
UIHandle::Result SampleHandle::Click
|
||||
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
|
||||
{
|
||||
const wxMouseEvent &event = evt.event;
|
||||
const wxRect &rect = evt.rect;
|
||||
const ViewInfo &viewInfo = pProject->GetViewInfo();
|
||||
Track *const pTrack = static_cast<Track*>(evt.pCell);
|
||||
|
||||
using namespace RefreshCode;
|
||||
|
||||
/// Someone has just clicked the mouse. What do we do?
|
||||
const bool unsafe = pProject->IsAudioActive();
|
||||
if (unsafe)
|
||||
return Cancelled;
|
||||
if (!IsSampleEditingPossible
|
||||
(event, rect, viewInfo, pTrack, rect.width))
|
||||
return Cancelled;
|
||||
|
||||
/// We're in a track view and zoomed enough to see the samples.
|
||||
mClickedTrack = static_cast<WaveTrack*>(pTrack);
|
||||
mRect = rect;
|
||||
|
||||
//If we are still around, we are drawing in earnest. Set some member data structures up:
|
||||
//First, calculate the starting sample. To get this, we need the time
|
||||
const double t0 =
|
||||
adjustTime(mClickedTrack, viewInfo.PositionToTime(event.m_x, rect.x));
|
||||
|
||||
//convert t0 to samples
|
||||
mClickedStartSample = mClickedTrack->TimeToLongSamples(t0);
|
||||
|
||||
//Determine how drawing should occur. If alt is down,
|
||||
//do a smoothing, instead of redrawing.
|
||||
if (event.m_altDown)
|
||||
{
|
||||
mAltKey = true;
|
||||
//*************************************************
|
||||
//*** ALT-DOWN-CLICK (SAMPLE SMOOTHING) ***
|
||||
//*************************************************
|
||||
//
|
||||
// Smoothing works like this: There is a smoothing kernel radius constant that
|
||||
// determines how wide the averaging window is. Plus, there is a smoothing brush radius,
|
||||
// which determines how many pixels wide around the selected pixel this smoothing is applied.
|
||||
//
|
||||
// Samples will be replaced by a mixture of the original points and the smoothed points,
|
||||
// with a triangular mixing probability whose value at the center point is
|
||||
// SMOOTHING_PROPORTION_MAX and at the far bounds is SMOOTHING_PROPORTION_MIN
|
||||
|
||||
//Get the region of samples around the selected point
|
||||
size_t sampleRegionSize = 1 + 2 * (SMOOTHING_KERNEL_RADIUS + SMOOTHING_BRUSH_RADIUS);
|
||||
Floats sampleRegion{ sampleRegionSize };
|
||||
Floats newSampleRegion{ 1 + 2 * (size_t)SMOOTHING_BRUSH_RADIUS };
|
||||
|
||||
//Get a sample from the track to do some tricks on.
|
||||
mClickedTrack->Get((samplePtr)sampleRegion.get(), floatSample,
|
||||
mClickedStartSample - SMOOTHING_KERNEL_RADIUS - SMOOTHING_BRUSH_RADIUS,
|
||||
sampleRegionSize);
|
||||
|
||||
//Go through each point of the smoothing brush and apply a smoothing operation.
|
||||
for (auto jj = -SMOOTHING_BRUSH_RADIUS; jj <= SMOOTHING_BRUSH_RADIUS; ++jj) {
|
||||
float sumOfSamples = 0;
|
||||
for (auto ii = -SMOOTHING_KERNEL_RADIUS; ii <= SMOOTHING_KERNEL_RADIUS; ++ii) {
|
||||
//Go through each point of the smoothing kernel and find the average
|
||||
|
||||
//The average is a weighted average, scaled by a weighting kernel that is simply triangular
|
||||
// A triangular kernel across N items, with a radius of R ( 2 R + 1 points), if the farthest:
|
||||
// points have a probability of a, the entire triangle has total probability of (R + 1)^2.
|
||||
// For sample number ii and middle brush sample M, (R + 1 - abs(M-ii))/ ((R+1)^2) gives a
|
||||
// legal distribution whose total probability is 1.
|
||||
//
|
||||
//
|
||||
// weighting factor value
|
||||
sumOfSamples +=
|
||||
(SMOOTHING_KERNEL_RADIUS + 1 - abs(ii)) *
|
||||
sampleRegion[ii + jj + SMOOTHING_KERNEL_RADIUS + SMOOTHING_BRUSH_RADIUS];
|
||||
|
||||
}
|
||||
newSampleRegion[jj + SMOOTHING_BRUSH_RADIUS] =
|
||||
sumOfSamples /
|
||||
((SMOOTHING_KERNEL_RADIUS + 1) *(SMOOTHING_KERNEL_RADIUS + 1));
|
||||
}
|
||||
|
||||
|
||||
// Now that the NEW sample levels are determined, go through each and mix it appropriately
|
||||
// with the original point, according to a 2-part linear function whose center has probability
|
||||
// SMOOTHING_PROPORTION_MAX and extends out SMOOTHING_BRUSH_RADIUS, at which the probability is
|
||||
// SMOOTHING_PROPORTION_MIN. _MIN and _MAX specify how much of the smoothed curve make it through.
|
||||
|
||||
float prob;
|
||||
|
||||
for (auto jj = -SMOOTHING_BRUSH_RADIUS; jj <= SMOOTHING_BRUSH_RADIUS; ++jj) {
|
||||
|
||||
prob =
|
||||
SMOOTHING_PROPORTION_MAX -
|
||||
(float)abs(jj) / SMOOTHING_BRUSH_RADIUS *
|
||||
(SMOOTHING_PROPORTION_MAX - SMOOTHING_PROPORTION_MIN);
|
||||
|
||||
newSampleRegion[jj + SMOOTHING_BRUSH_RADIUS] =
|
||||
newSampleRegion[jj + SMOOTHING_BRUSH_RADIUS] * prob +
|
||||
sampleRegion[SMOOTHING_BRUSH_RADIUS + SMOOTHING_KERNEL_RADIUS + jj] *
|
||||
(1 - prob);
|
||||
}
|
||||
//Set the sample to the point of the mouse event
|
||||
mClickedTrack->Set((samplePtr)newSampleRegion.get(), floatSample,
|
||||
mClickedStartSample - SMOOTHING_BRUSH_RADIUS, 1 + 2 * SMOOTHING_BRUSH_RADIUS);
|
||||
|
||||
// mLastDragSampleValue will not be used
|
||||
}
|
||||
else
|
||||
{
|
||||
mAltKey = false;
|
||||
//*************************************************
|
||||
//*** PLAIN DOWN-CLICK (NORMAL DRAWING) ***
|
||||
//*************************************************
|
||||
|
||||
//Otherwise (e.g., the alt button is not down) do normal redrawing, based on the mouse position.
|
||||
const float newLevel = FindSampleEditingLevel(event, viewInfo, t0);
|
||||
|
||||
//Set the sample to the point of the mouse event
|
||||
mClickedTrack->Set((samplePtr)&newLevel, floatSample, mClickedStartSample, 1);
|
||||
|
||||
mLastDragSampleValue = newLevel;
|
||||
}
|
||||
|
||||
//Set the member data structures for drawing
|
||||
mLastDragSample = mClickedStartSample;
|
||||
|
||||
// Sample data changed on either branch, so refresh the track display.
|
||||
return RefreshCell;
|
||||
}
|
||||
|
||||
UIHandle::Result SampleHandle::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;
|
||||
}
|
||||
|
||||
//*************************************************
|
||||
//*** DRAG-DRAWING ***
|
||||
//*************************************************
|
||||
|
||||
//No dragging effects if the alt key is down--
|
||||
//Don't allow left-right dragging for smoothing operation
|
||||
if (mAltKey)
|
||||
return RefreshNone;
|
||||
|
||||
sampleCount s0; //declare this for use below. It designates which sample number to draw.
|
||||
|
||||
// Figure out what time the click was at
|
||||
//Find the point that we want to redraw at. If the control button is down,
|
||||
//adjust only the originally clicked-on sample
|
||||
|
||||
if (event.m_controlDown) {
|
||||
//*************************************************
|
||||
//*** CTRL-DOWN (Hold Initial Sample Constant ***
|
||||
//*************************************************
|
||||
|
||||
s0 = mClickedStartSample;
|
||||
}
|
||||
else {
|
||||
//*************************************************
|
||||
//*** Normal CLICK-drag (Normal drawing) ***
|
||||
//*************************************************
|
||||
|
||||
//Otherwise, adjust the sample you are dragging over right now.
|
||||
//convert this to samples
|
||||
const double tt = viewInfo.PositionToTime(event.m_x, mRect.x);
|
||||
s0 = mClickedTrack->TimeToLongSamples(tt);
|
||||
}
|
||||
|
||||
const double t0 = mClickedTrack->LongSamplesToTime(s0);
|
||||
|
||||
// Do redrawing, based on the mouse position.
|
||||
// Calculate where the mouse is located vertically (between +/- 1)
|
||||
|
||||
const float newLevel = FindSampleEditingLevel(event, viewInfo, t0);
|
||||
|
||||
//Now, redraw all samples between current and last redrawn sample, inclusive
|
||||
//Go from the smaller to larger sample.
|
||||
const auto start = std::min(s0, mLastDragSample);
|
||||
const auto end = std::max(s0, mLastDragSample);
|
||||
// Few enough samples to be drawn individually on screen will not
|
||||
// overflow size_t:
|
||||
const auto size = ( end - start + 1 ).as_size_t();
|
||||
if (size == 1) {
|
||||
mClickedTrack->Set((samplePtr)&newLevel, floatSample, start, size);
|
||||
}
|
||||
else {
|
||||
std::vector<float> values(size);
|
||||
for (auto ii = start; ii <= end; ++ii) {
|
||||
//This interpolates each sample linearly:
|
||||
// i - start will not overflow size_t either:
|
||||
values[( ii - start ).as_size_t()] =
|
||||
mLastDragSampleValue + (newLevel - mLastDragSampleValue) *
|
||||
(ii - mLastDragSample).as_float() /
|
||||
(s0 - mLastDragSample).as_float();
|
||||
}
|
||||
mClickedTrack->Set((samplePtr)&values[0], floatSample, start, size);
|
||||
}
|
||||
|
||||
//Update the member data structures.
|
||||
mLastDragSample = s0;
|
||||
mLastDragSampleValue = newLevel;
|
||||
|
||||
return RefreshCell;
|
||||
}
|
||||
|
||||
HitTestPreview SampleHandle::Preview
|
||||
(const TrackPanelMouseEvent &evt, const AudacityProject *pProject)
|
||||
{
|
||||
return HitPreview(evt.event, pProject, false);
|
||||
}
|
||||
|
||||
UIHandle::Result SampleHandle::Release
|
||||
(const TrackPanelMouseEvent &, AudacityProject *pProject,
|
||||
wxWindow *)
|
||||
{
|
||||
const bool unsafe = pProject->IsAudioActive();
|
||||
if (unsafe)
|
||||
return this->Cancel(pProject);
|
||||
|
||||
//*************************************************
|
||||
//*** UP-CLICK (Finish drawing) ***
|
||||
//*************************************************
|
||||
//On up-click, send the state to the undo stack
|
||||
mClickedTrack = nullptr; //Set this to NULL so it will catch improper drag events.
|
||||
pProject->PushState(_("Moved Samples"),
|
||||
_("Sample Edit"),
|
||||
UndoPush::CONSOLIDATE | UndoPush::AUTOSAVE);
|
||||
|
||||
// No change to draw since last drag
|
||||
return RefreshCode::RefreshNone;
|
||||
}
|
||||
|
||||
UIHandle::Result SampleHandle::Cancel(AudacityProject *pProject)
|
||||
{
|
||||
pProject->RollbackState();
|
||||
mClickedTrack = nullptr;
|
||||
return RefreshCode::RefreshCell;
|
||||
}
|
||||
|
||||
float SampleHandle::FindSampleEditingLevel
|
||||
(const wxMouseEvent &event, const ViewInfo &, double t0)
|
||||
{
|
||||
// Calculate where the mouse is located vertically (between +/- 1)
|
||||
float zoomMin, zoomMax;
|
||||
mClickedTrack->GetDisplayBounds(&zoomMin, &zoomMax);
|
||||
|
||||
const int yy = event.m_y - mRect.y;
|
||||
const int height = mRect.GetHeight();
|
||||
const bool dB = !mClickedTrack->GetWaveformSettings().isLinear();
|
||||
float newLevel =
|
||||
::ValueOfPixel(yy, height, false, dB,
|
||||
mClickedTrack->GetWaveformSettings().dBRange, zoomMin, zoomMax);
|
||||
|
||||
//Take the envelope into account
|
||||
Envelope *const env = mClickedTrack->GetEnvelopeAtX(event.m_x);
|
||||
if (env)
|
||||
{
|
||||
// Calculate sample as it would be rendered, so quantize time
|
||||
double envValue = env->GetValue( t0, 1.0 / mClickedTrack->GetRate());
|
||||
if (envValue > 0)
|
||||
newLevel /= envValue;
|
||||
else
|
||||
newLevel = 0;
|
||||
|
||||
//Make sure the NEW level is between +/-1
|
||||
newLevel = std::max(-1.0f, std::min(1.0f, newLevel));
|
||||
}
|
||||
|
||||
return newLevel;
|
||||
}
|
||||
74
src/tracks/playabletrack/wavetrack/ui/SampleHandle.h
Normal file
74
src/tracks/playabletrack/wavetrack/ui/SampleHandle.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
SampleHandle.h
|
||||
|
||||
Paul Licameli
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef __AUDACITY_SAMPLE_HANDLE__
|
||||
#define __AUDACITY_SAMPLE_HANDLE__
|
||||
|
||||
#include "../../../../UIHandle.h"
|
||||
#include "audacity/Types.h"
|
||||
|
||||
class wxMouseEvent;
|
||||
#include <wx/gdicmn.h>
|
||||
|
||||
struct HitTestResult;
|
||||
class Track;
|
||||
class ViewInfo;
|
||||
class WaveTrack;
|
||||
|
||||
class SampleHandle final : public UIHandle
|
||||
{
|
||||
SampleHandle();
|
||||
SampleHandle(const SampleHandle&) = delete;
|
||||
SampleHandle &operator=(const SampleHandle&) = delete;
|
||||
static SampleHandle& Instance();
|
||||
static HitTestPreview HitPreview
|
||||
(const wxMouseEvent &event, const AudacityProject *pProject, bool unsafe);
|
||||
|
||||
public:
|
||||
static HitTestResult HitAnywhere
|
||||
(const wxMouseEvent &event, const AudacityProject *pProject);
|
||||
static HitTestResult HitTest
|
||||
(const wxMouseEvent &event, const wxRect &rect,
|
||||
const AudacityProject *pProject, Track *pTrack);
|
||||
|
||||
virtual ~SampleHandle();
|
||||
|
||||
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:
|
||||
float FindSampleEditingLevel
|
||||
(const wxMouseEvent &event, const ViewInfo &viewInfo, double t0);
|
||||
|
||||
WaveTrack *mClickedTrack{};
|
||||
wxRect mRect{};
|
||||
|
||||
sampleCount mClickedStartSample{};
|
||||
sampleCount mLastDragSample{};
|
||||
float mLastDragSampleValue{};
|
||||
bool mAltKey{};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -13,12 +13,28 @@ Paul Licameli split from TrackPanel.cpp
|
||||
#include "WaveTrackVRulerControls.h"
|
||||
|
||||
#include "../../../../HitTestResult.h"
|
||||
#include "../../../../Project.h"
|
||||
#include "../../../../TrackPanelMouseEvent.h"
|
||||
#include "../../../../toolbars/ToolsToolBar.h"
|
||||
|
||||
#include "SampleHandle.h"
|
||||
|
||||
HitTestResult WaveTrack::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)) {
|
||||
if (NULL != (result =
|
||||
SampleHandle::HitTest(event.event, event.rect, pProject, this)).preview.cursor)
|
||||
;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TrackControls *WaveTrack::GetControls()
|
||||
|
||||
@@ -17,6 +17,7 @@ Paul Licameli split from TrackPanel.cpp
|
||||
#include "../../Project.h"
|
||||
#include "../../toolbars/ToolsToolBar.h"
|
||||
|
||||
#include "../playabletrack/wavetrack/ui/SampleHandle.h"
|
||||
#include "ZoomHandle.h"
|
||||
|
||||
HitTestResult Track::HitTest
|
||||
@@ -28,12 +29,13 @@ HitTestResult Track::HitTest
|
||||
const bool isMultiTool = pTtb->IsDown(multiTool);
|
||||
if (!isMultiTool) {
|
||||
switch (pTtb->GetCurrentTool()) {
|
||||
case drawTool:
|
||||
return SampleHandle::HitAnywhere(event.event, pProject);
|
||||
case zoomTool:
|
||||
return ZoomHandle::HitAnywhere(event.event, pProject);
|
||||
|
||||
case selectTool:
|
||||
case envelopeTool:
|
||||
case drawTool:
|
||||
case slideTool:
|
||||
default:
|
||||
// cases not yet implemented
|
||||
|
||||
Reference in New Issue
Block a user