From 2c1a16f593efaebe5dbdae749a27346cb6b41587 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Wed, 5 Jul 2017 16:45:55 -0400 Subject: [PATCH] Changed lifetime management of UIHandle objects, no singletons... ... Rather, construct them during hit tests (also capturing more state sooner rather than at Click time, and adding some accessors for later use) This also fixes bug 1677 by other means and avoids similar problems. A cell may be implemented to re-use a previously hit handle object, not yet clicked, in a later hit test, by remembering a weak pointer, but TrackPanel holds the strong pointers that determine when the object is destroyed. And the objects will surely be destroyed after drag-release, or ESC key. For now they are also destroyed whenever not dragging, and hit-testing is re-invoked; that will be changed later, so that the re-use mentioned above becomes effective, but still they will be destroyed when the pointer moves from one cell to another. --- src/HitTestResult.h | 7 +- src/LabelTrack.h | 5 + src/NoteTrack.cpp | 17 ++- src/NoteTrack.h | 5 + src/Project.h | 2 + src/TimeTrack.h | 4 + src/Track.h | 6 + src/TrackPanel.cpp | 10 +- src/TrackPanel.h | 4 +- src/TrackPanelCell.h | 1 - src/TrackPanelResizeHandle.cpp | 49 ++------ src/TrackPanelResizeHandle.h | 11 +- src/TrackPanelResizerCell.cpp | 21 +++- src/TrackPanelResizerCell.h | 5 +- src/UIHandle.cpp | 1 + src/UIHandle.h | 24 ++++ src/WaveTrack.h | 8 ++ .../labeltrack/ui/LabelDefaultClickHandle.cpp | 2 +- .../labeltrack/ui/LabelDefaultClickHandle.h | 5 +- src/tracks/labeltrack/ui/LabelGlyphHandle.cpp | 33 +++-- src/tracks/labeltrack/ui/LabelGlyphHandle.h | 12 +- src/tracks/labeltrack/ui/LabelTextHandle.cpp | 32 ++--- src/tracks/labeltrack/ui/LabelTextHandle.h | 16 ++- src/tracks/labeltrack/ui/LabelTrackUI.cpp | 6 +- .../notetrack/ui/NoteTrackButtonHandle.cpp | 37 +++--- .../notetrack/ui/NoteTrackButtonHandle.h | 18 ++- .../notetrack/ui/NoteTrackControls.cpp | 18 ++- .../notetrack/ui/NoteTrackControls.h | 11 ++ .../notetrack/ui/NoteTrackSliderHandles.cpp | 27 ++-- .../notetrack/ui/NoteTrackSliderHandles.h | 17 ++- .../notetrack/ui/NoteTrackUI.cpp | 4 +- .../notetrack/ui/NoteTrackVRulerControls.cpp | 5 +- .../notetrack/ui/NoteTrackVRulerControls.h | 5 + .../notetrack/ui/NoteTrackVZoomHandle.cpp | 38 +++--- .../notetrack/ui/NoteTrackVZoomHandle.h | 15 ++- .../notetrack/ui/StretchHandle.cpp | 30 ++--- .../notetrack/ui/StretchHandle.h | 14 ++- .../ui/PlayableTrackButtonHandles.cpp | 44 +++---- .../ui/PlayableTrackButtonHandles.h | 26 ++-- .../wavetrack/ui/CutlineHandle.cpp | 61 ++++----- .../wavetrack/ui/CutlineHandle.h | 28 +++-- .../wavetrack/ui/SampleHandle.cpp | 35 +++--- .../playabletrack/wavetrack/ui/SampleHandle.h | 16 ++- .../wavetrack/ui/WaveTrackControls.cpp | 18 ++- .../wavetrack/ui/WaveTrackControls.h | 11 ++ .../wavetrack/ui/WaveTrackSliderHandles.cpp | 53 ++++---- .../wavetrack/ui/WaveTrackSliderHandles.h | 34 +++-- .../wavetrack/ui/WaveTrackUI.cpp | 41 +++--- .../wavetrack/ui/WaveTrackVRulerControls.cpp | 9 +- .../wavetrack/ui/WaveTrackVRulerControls.h | 5 + .../wavetrack/ui/WaveTrackVZoomHandle.cpp | 31 ++--- .../wavetrack/ui/WaveTrackVZoomHandle.h | 12 +- src/tracks/timetrack/ui/TimeTrackUI.cpp | 2 +- src/tracks/ui/BackgroundCell.cpp | 117 +++++++++--------- src/tracks/ui/BackgroundCell.h | 10 ++ src/tracks/ui/ButtonHandle.cpp | 15 +-- src/tracks/ui/ButtonHandle.h | 11 +- src/tracks/ui/EnvelopeHandle.cpp | 56 ++++----- src/tracks/ui/EnvelopeHandle.h | 30 +++-- src/tracks/ui/SelectHandle.cpp | 58 ++++++--- src/tracks/ui/SelectHandle.h | 6 +- src/tracks/ui/SliderHandle.cpp | 9 +- src/tracks/ui/SliderHandle.h | 12 +- src/tracks/ui/TimeShiftHandle.cpp | 32 ++--- src/tracks/ui/TimeShiftHandle.h | 24 +++- src/tracks/ui/TrackButtonHandles.cpp | 72 +++++------ src/tracks/ui/TrackButtonHandles.h | 49 +++++--- src/tracks/ui/TrackControls.cpp | 18 ++- src/tracks/ui/TrackControls.h | 10 ++ src/tracks/ui/TrackSelectHandle.cpp | 26 ++-- src/tracks/ui/TrackSelectHandle.h | 11 +- src/tracks/ui/TrackUI.cpp | 13 +- src/tracks/ui/TrackVRulerControls.cpp | 4 +- src/tracks/ui/ZoomHandle.cpp | 21 ++-- src/tracks/ui/ZoomHandle.h | 14 ++- 75 files changed, 891 insertions(+), 678 deletions(-) diff --git a/src/HitTestResult.h b/src/HitTestResult.h index 2f6770222..7ddf15569 100644 --- a/src/HitTestResult.h +++ b/src/HitTestResult.h @@ -12,10 +12,13 @@ Paul Licameli #define __AUDACITY_HIT_TEST_RESULT__ #include +#include "MemoryX.h" class UIHandle; class wxCursor; +using UIHandlePtr = std::shared_ptr; + struct HitTestPreview { HitTestPreview() @@ -39,12 +42,12 @@ struct HitTestResult HitTestResult() {} - HitTestResult(HitTestPreview preview_, UIHandle *handle_) + HitTestResult(HitTestPreview preview_, UIHandlePtr handle_) : preview(preview_), handle(handle_) {} HitTestPreview preview {}; - UIHandle *handle {}; + UIHandlePtr handle {}; }; #endif diff --git a/src/LabelTrack.h b/src/LabelTrack.h index 037b32949..353b51b29 100644 --- a/src/LabelTrack.h +++ b/src/LabelTrack.h @@ -105,6 +105,8 @@ const int NUM_GLYPH_CONFIGS = 3; const int NUM_GLYPH_HIGHLIGHTS = 4; const int MAX_NUM_ROWS =80; +class LabelGlyphHandle; +class LabelTextHandle; class AUDACITY_DLL_API LabelTrack final : public Track { @@ -317,6 +319,9 @@ private: static wxFont msFont; + std::weak_ptr mGlyphHandle; + std::weak_ptr mTextHandle; + protected: std::shared_ptr GetControls() override; std::shared_ptr GetVRulerControls() override; diff --git a/src/NoteTrack.cpp b/src/NoteTrack.cpp index f7b8481d7..dddd76e99 100644 --- a/src/NoteTrack.cpp +++ b/src/NoteTrack.cpp @@ -336,11 +336,7 @@ void NoteTrack::DrawLabelControls AColor::MIDIChannel(&dc, 0); // always return with gray color selected } -// Handles clicking within the midi controls rect (same as DrawLabelControls). -// This is somewhat oddly written, as these aren't real buttons - they act -// when the mouse goes down; you can't hold it pressed and move off of it. -// Left-clicking toggles a single channel; right-clicking turns off all other channels. -bool NoteTrack::LabelClick(const wxRect &rect, int mx, int my, bool right) +int NoteTrack::FindChannel(const wxRect &rect, int mx, int my) { wxASSERT_MSG(rect.width % 4 == 0, "Midi channel control rect width must be divisible by 4"); wxASSERT_MSG(rect.height % 4 == 0, "Midi channel control rect height must be divisible by 4"); @@ -351,8 +347,17 @@ bool NoteTrack::LabelClick(const wxRect &rect, int mx, int my, bool right) int col = (mx - rect.x) / cellWidth; int row = (my - rect.y) / cellHeight; - int channel = row * 4 + col; + return row * 4 + col; +} + +// Handles clicking within the midi controls rect (same as DrawLabelControls). +// This is somewhat oddly written, as these aren't real buttons - they act +// when the mouse goes down; you can't hold it pressed and move off of it. +// Left-clicking toggles a single channel; right-clicking turns off all other channels. +bool NoteTrack::LabelClick(const wxRect &rect, int mx, int my, bool right) +{ + auto channel = FindChannel(rect, mx, my); if (right) SoloVisibleChan(channel); else diff --git a/src/NoteTrack.h b/src/NoteTrack.h index 0aeafcb84..2e408ab0a 100644 --- a/src/NoteTrack.h +++ b/src/NoteTrack.h @@ -61,6 +61,8 @@ using NoteTrackBase = using QuantizedTimeAndBeat = std::pair< double, double >; +class StretchHandle; + class AUDACITY_DLL_API NoteTrack final : public NoteTrackBase { @@ -89,6 +91,7 @@ class AUDACITY_DLL_API NoteTrack final static void DrawLabelControls ( const NoteTrack *pTrack, wxDC & dc, const wxRect &rect ); + int FindChannel(const wxRect &rect, int mx, int my); bool LabelClick(const wxRect &rect, int x, int y, bool right); void SetSequence(std::unique_ptr &&seq); @@ -242,6 +245,8 @@ class AUDACITY_DLL_API NoteTrack final int mVisibleChannels; // bit set of visible channels int mLastMidiPosition; + std::weak_ptr mStretchHandle; + protected: std::shared_ptr GetControls() override; std::shared_ptr GetVRulerControls() override; diff --git a/src/Project.h b/src/Project.h index 769c81128..bd36268a8 100644 --- a/src/Project.h +++ b/src/Project.h @@ -806,6 +806,8 @@ public: public: PlaybackScroller &GetPlaybackScroller() { return *mPlaybackScroller; } + std::shared_ptr GetBackgroundCell() const + { return mBackgroundCell; } DECLARE_EVENT_TABLE() }; diff --git a/src/TimeTrack.h b/src/TimeTrack.h index beea43909..9a9b558a8 100644 --- a/src/TimeTrack.h +++ b/src/TimeTrack.h @@ -23,6 +23,8 @@ class Envelope; class Ruler; class ZoomInfo; +class EnvelopeHandle; + class TimeTrack final : public Track { public: @@ -141,6 +143,8 @@ class TimeTrack final : public Track { bool mDisplayLog; bool mRescaleXMLValues; // needed for backward-compatibility with older project files + std::weak_ptr mEnvelopeHandle; + /** @brief Copy the metadata from another track but not the points * * Copies the Name, DefaultName, Range and Display data from the source track diff --git a/src/Track.h b/src/Track.h index 5c4ba605a..137ddd68b 100644 --- a/src/Track.h +++ b/src/Track.h @@ -44,6 +44,9 @@ class NoteTrack; class AudacityProject; class ZoomInfo; +class SelectHandle; +class TimeShiftHandle; + WX_DEFINE_USER_EXPORTED_ARRAY(Track*, TrackArray, class AUDACITY_DLL_API); using WaveTrackArray = std::vector < WaveTrack* > ; @@ -318,6 +321,9 @@ protected: std::shared_ptr mpControls; std::shared_ptr mpVRulerContols; std::shared_ptr mpResizer; + + std::weak_ptr mSelectHandle; + std::weak_ptr mTimeShiftHandle; }; class AUDACITY_DLL_API AudioTrack /* not final */ : public Track diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index cb4ec1393..cd48714bf 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -770,7 +770,7 @@ void TrackPanel::CancelDragging() ProcessUIHandleResult( this, mRuler, pTrack.get(), NULL, refreshResult); mpClickedTrack.reset(); - mUIHandle = NULL; + mUIHandle.reset(); Uncapture(); } } @@ -1465,7 +1465,7 @@ try (this, mRuler, pClickedTrack.get(), pTrack.get(), refreshResult); if (refreshResult & RefreshCode::Cancelled) { // Drag decided to abort itself - mUIHandle = NULL; + mUIHandle.reset(); mpClickedTrack.reset(); Uncapture( &event ); } @@ -1484,7 +1484,7 @@ try // Null this pointer out first before calling Release -- because on Windows, we can // come back recursively to this place during handling of the context menu, // because of a capture lost event. - mUIHandle = nullptr; + mUIHandle.reset(); UIHandle::Result refreshResult = uiHandle->Release( tpmEvent, GetProject(), this ); ProcessUIHandleResult @@ -1553,7 +1553,7 @@ void TrackPanel::HandleClick( const TrackPanelMouseEvent &tpmEvent ) UIHandle::Result refreshResult = mUIHandle->Click( tpmEvent, GetProject() ); if (refreshResult & RefreshCode::Cancelled) - mUIHandle = NULL; + mUIHandle.reset(); else mpClickedTrack = pTrack; ProcessUIHandleResult @@ -3113,7 +3113,7 @@ TrackPanelCellIterator::TrackPanelCellIterator(TrackPanel *trackPanel, bool begi mpCell = mpTrack; else mpCell = trackPanel->GetBackgroundCell(); - } + } const auto size = mPanel->GetSize(); mRect = { 0, 0, size.x, size.y }; diff --git a/src/TrackPanel.h b/src/TrackPanel.h index 1c047d65b..1259c204e 100644 --- a/src/TrackPanel.h +++ b/src/TrackPanel.h @@ -54,6 +54,7 @@ class NoteTrack; class WaveTrack; class WaveClip; class UIHandle; +using UIHandlePtr = std::shared_ptr; // Declared elsewhere, to reduce compilation dependencies class TrackPanelListener; @@ -528,8 +529,7 @@ protected: protected: std::weak_ptr mpClickedTrack; - // TrackPanel is not responsible for memory management: - UIHandle *mUIHandle {}; + UIHandlePtr mUIHandle; std::shared_ptr mpBackground; diff --git a/src/TrackPanelCell.h b/src/TrackPanelCell.h index 45a9c6569..96abf115e 100644 --- a/src/TrackPanelCell.h +++ b/src/TrackPanelCell.h @@ -32,7 +32,6 @@ public: // appropriate to the mouse position, modifier keys, and button-down state. // The button-down state passed to the function is as it will be at click // time -- not necessarily as it is now. - // TrackPanel is not responsible for memory management of the handler. virtual HitTestResult HitTest (const TrackPanelMouseState &state, const AudacityProject *pProject) = 0; diff --git a/src/TrackPanelResizeHandle.cpp b/src/TrackPanelResizeHandle.cpp index 5d03223d6..5b16ffacc 100644 --- a/src/TrackPanelResizeHandle.cpp +++ b/src/TrackPanelResizeHandle.cpp @@ -28,16 +28,6 @@ Paul Licameli split from TrackPanel.cpp #include "WaveTrack.h" #endif -TrackPanelResizeHandle::TrackPanelResizeHandle() -{ -} - -TrackPanelResizeHandle &TrackPanelResizeHandle::Instance() -{ - static TrackPanelResizeHandle instance; - return instance; -} - HitTestPreview TrackPanelResizeHandle::HitPreview(bool bLinked) { static wxCursor resizeCursor{ wxCURSOR_SIZENS }; @@ -69,30 +59,14 @@ TrackPanelResizeHandle::~TrackPanelResizeHandle() UIHandle::Result TrackPanelResizeHandle::Click (const TrackPanelMouseEvent &evt, AudacityProject *pProject) { - const wxMouseEvent &event = evt.event; - const auto pCell = - static_cast(evt.pCell.get()); - auto track = pCell->FindTrack().get(); - if (track && dynamic_cast< TrackControls * >( pCell )) { - // Clicked under a label; - // if stereo, replace left channel with the right: - if (track && track->GetLinked()) - track = track->GetLink(); - } - if (!track) - return RefreshCode::Cancelled; - - mpTrack = Track::Pointer( track ); - - /// ButtonDown means they just clicked and haven't released yet. - /// We use this opportunity to save which track they clicked on, - /// and the initial height of the track, so as they drag we can - /// update the track size. - - mMouseClickY = event.m_y; - - TrackList *const tracks = pProject->GetTracks(); + return RefreshCode::RefreshNone; +} +TrackPanelResizeHandle::TrackPanelResizeHandle +( const std::shared_ptr &track, int y, const AudacityProject *pProject ) + : mpTrack{ track } + , mMouseClickY( y ) +{ #ifdef EXPERIMENTAL_OUTPUT_DISPLAY if (MONO_WAVE_PAN(track)){ //STM: Determine whether we should rescale one or two tracks @@ -116,11 +90,12 @@ UIHandle::Result TrackPanelResizeHandle::Click else #endif { - Track *prev = tracks->GetPrev(track); - Track *next = tracks->GetNext(track); + auto tracks = pProject->GetTracks(); + Track *prev = tracks->GetPrev(track.get()); + Track *next = tracks->GetNext(track.get()); //STM: Determine whether we should rescale one or two tracks - if (prev && prev->GetLink() == track) { + if (prev && prev->GetLink() == track.get()) { // mpTrack is the lower track mInitialTrackHeight = track->GetHeight(); mInitialActualHeight = track->GetActualHeight(); @@ -146,8 +121,6 @@ UIHandle::Result TrackPanelResizeHandle::Click mMode = IsResizing; } } - - return RefreshCode::RefreshNone; } UIHandle::Result TrackPanelResizeHandle::Drag diff --git a/src/TrackPanelResizeHandle.h b/src/TrackPanelResizeHandle.h index 4013df19a..752722a9c 100644 --- a/src/TrackPanelResizeHandle.h +++ b/src/TrackPanelResizeHandle.h @@ -20,16 +20,21 @@ class TrackPanelCellIterator; class TrackPanelResizeHandle final : public UIHandle { - TrackPanelResizeHandle(); TrackPanelResizeHandle(const TrackPanelResizeHandle&) = delete; - TrackPanelResizeHandle &operator=(const TrackPanelResizeHandle&) = delete; public: - static TrackPanelResizeHandle& Instance(); + explicit TrackPanelResizeHandle + ( const std::shared_ptr &pTrack, int y, + const AudacityProject *pProject ); + + TrackPanelResizeHandle &operator=(const TrackPanelResizeHandle&) = default; + static HitTestPreview HitPreview(bool bLinked); virtual ~TrackPanelResizeHandle(); + std::shared_ptr GetTrack() const { return mpTrack.lock(); } + Result Click (const TrackPanelMouseEvent &event, AudacityProject *pProject) override; diff --git a/src/TrackPanelResizerCell.cpp b/src/TrackPanelResizerCell.cpp index d01c728ee..c12ca1b8e 100644 --- a/src/TrackPanelResizerCell.cpp +++ b/src/TrackPanelResizerCell.cpp @@ -10,18 +10,29 @@ Paul Licameli split from TrackPanel.cpp #include "Audacity.h" #include "TrackPanelResizerCell.h" + #include "TrackPanelResizeHandle.h" +#include "TrackPanelMouseEvent.h" #include "HitTestResult.h" +#include + TrackPanelResizerCell::TrackPanelResizerCell( std::shared_ptr pTrack ) : mpTrack{ pTrack } {} HitTestResult TrackPanelResizerCell::HitTest -(const TrackPanelMouseState &, const AudacityProject *) +(const TrackPanelMouseState &st, const AudacityProject *pProject) { - return { - TrackPanelResizeHandle::HitPreview( mBetweenTracks ), - &TrackPanelResizeHandle::Instance() - }; + auto pTrack = mpTrack.lock(); + if (pTrack) { + auto result = std::make_shared( + pTrack, st.state.m_y, pProject ); + result = AssignUIHandlePtr(mResizeHandle, result); + return { + result->Preview(st, pProject), + result + }; + } + return {}; } diff --git a/src/TrackPanelResizerCell.h b/src/TrackPanelResizerCell.h index 2565eec4d..5d8556a6b 100644 --- a/src/TrackPanelResizerCell.h +++ b/src/TrackPanelResizerCell.h @@ -13,6 +13,8 @@ #include "tracks/ui/CommonTrackPanelCell.h" +class TrackPanelResizeHandle; + class TrackPanelResizerCell : public CommonTrackPanelCell { TrackPanelResizerCell(const TrackPanelResizerCell&) = delete; @@ -29,7 +31,8 @@ public: private: friend class TrackPanelCellIterator; std::weak_ptr mpTrack; - bool mBetweenTracks {}; + + std::weak_ptr mResizeHandle; }; #endif diff --git a/src/UIHandle.cpp b/src/UIHandle.cpp index 0b0a621ce..1b3fb3b52 100644 --- a/src/UIHandle.cpp +++ b/src/UIHandle.cpp @@ -8,6 +8,7 @@ Paul Licameli **********************************************************************/ +#include "Audacity.h" #include "UIHandle.h" UIHandle::~UIHandle() diff --git a/src/UIHandle.h b/src/UIHandle.h index 2f2dfcab6..2315d95bb 100644 --- a/src/UIHandle.h +++ b/src/UIHandle.h @@ -24,6 +24,8 @@ class TrackPanelCell; struct TrackPanelMouseEvent; struct TrackPanelMouseState; +#include "MemoryX.h" + // A TrackPanelCell reports a handle object of some subclass, in response to a // hit test at a mouse position; then this handle processes certain events, // and maintains necessary state through click-drag-release event sequences. @@ -99,4 +101,26 @@ public: virtual void OnProjectChange(AudacityProject *pProject); }; +using UIHandlePtr = std::shared_ptr; + +// A frequent convenience +template +std::shared_ptr AssignUIHandlePtr +( std::weak_ptr &holder, const std::shared_ptr &pNew ) +{ + // Either assign to a null weak_ptr, or else rewrite what the weak_ptr + // points at. Thus a handle already pointed at changes its state but not its + // identity. This may matter for the framework that holds the strong + // pointers. + auto ptr = holder.lock(); + if (!ptr) { + holder = pNew; + return pNew; + } + else { + *ptr = std::move(*pNew); + return ptr; + } +} + #endif diff --git a/src/WaveTrack.h b/src/WaveTrack.h index 65a2d8ff2..0ded2945c 100644 --- a/src/WaveTrack.h +++ b/src/WaveTrack.h @@ -28,6 +28,10 @@ class SpectrogramSettings; class WaveformSettings; class TimeWarper; +class CutlineHandle; +class SampleHandle; +class EnvelopeHandle; + // // Tolerance for merging wave tracks (in seconds) // @@ -636,6 +640,10 @@ class AUDACITY_DLL_API WaveTrack final : public PlayableTrack { std::unique_ptr mpSpectrumSettings; std::unique_ptr mpWaveformSettings; + std::weak_ptr mCutlineHandle; + std::weak_ptr mSampleHandle; + std::weak_ptr mEnvelopeHandle; + protected: std::shared_ptr GetControls() override; std::shared_ptr GetVRulerControls() override; diff --git a/src/tracks/labeltrack/ui/LabelDefaultClickHandle.cpp b/src/tracks/labeltrack/ui/LabelDefaultClickHandle.cpp index 9922612c3..d2a8bb278 100644 --- a/src/tracks/labeltrack/ui/LabelDefaultClickHandle.cpp +++ b/src/tracks/labeltrack/ui/LabelDefaultClickHandle.cpp @@ -31,7 +31,7 @@ struct LabelDefaultClickHandle::LabelState { void LabelDefaultClickHandle::SaveState( AudacityProject *pProject ) { - mLabelState = std::make_unique(); + mLabelState = std::make_shared(); auto &pairs = mLabelState->mPairs; TrackList *const tracks = pProject->GetTracks(); TrackListIterator iter(tracks); diff --git a/src/tracks/labeltrack/ui/LabelDefaultClickHandle.h b/src/tracks/labeltrack/ui/LabelDefaultClickHandle.h index 3be5bece4..15ec85c2c 100644 --- a/src/tracks/labeltrack/ui/LabelDefaultClickHandle.h +++ b/src/tracks/labeltrack/ui/LabelDefaultClickHandle.h @@ -23,12 +23,13 @@ class LabelTrack; class LabelDefaultClickHandle /* not final */ : public UIHandle { LabelDefaultClickHandle(const LabelDefaultClickHandle&) = delete; - LabelDefaultClickHandle &operator=(const LabelDefaultClickHandle&) = delete; public: LabelDefaultClickHandle(); virtual ~LabelDefaultClickHandle(); + LabelDefaultClickHandle &operator=(LabelDefaultClickHandle&&) = default; + Result Click (const TrackPanelMouseEvent &event, AudacityProject *pProject) override; @@ -45,7 +46,7 @@ public: private: struct LabelState; - std::unique_ptr< LabelState > mLabelState; + std::shared_ptr< LabelState > mLabelState; void SaveState( AudacityProject *pProject ); void RestoreState( AudacityProject *pProject ); }; diff --git a/src/tracks/labeltrack/ui/LabelGlyphHandle.cpp b/src/tracks/labeltrack/ui/LabelGlyphHandle.cpp index 7bc322541..0a2956c84 100644 --- a/src/tracks/labeltrack/ui/LabelGlyphHandle.cpp +++ b/src/tracks/labeltrack/ui/LabelGlyphHandle.cpp @@ -23,18 +23,14 @@ Paul Licameli split from TrackPanel.cpp #include #include -LabelGlyphHandle::LabelGlyphHandle() -{ -} - -LabelGlyphHandle &LabelGlyphHandle::Instance() -{ - static LabelGlyphHandle instance; - return instance; -} +LabelGlyphHandle::LabelGlyphHandle +(const std::shared_ptr &pLT, const wxRect &rect) + : mpLT{ pLT } + , mRect{ rect } +{} HitTestPreview LabelGlyphHandle::HitPreview - (bool hitCenter, unsigned refreshResult) +(bool hitCenter, unsigned refreshResult) { static wxCursor arrowCursor{ wxCURSOR_ARROW }; return { @@ -49,7 +45,9 @@ HitTestPreview LabelGlyphHandle::HitPreview } HitTestResult LabelGlyphHandle::HitTest -(const wxMouseState &state, const std::shared_ptr &pLT) +(std::weak_ptr &holder, + const wxMouseState &state, + const std::shared_ptr &pLT, const wxRect &rect) { using namespace RefreshCode; unsigned refreshResult = RefreshNone; @@ -70,16 +68,18 @@ HitTestResult LabelGlyphHandle::HitTest // signal this by setting the tip. if (edge != 0) { + auto result = std::make_shared( pLT, rect ); + result = AssignUIHandlePtr(holder, result); return { HitPreview(pLT->mbHitCenter, refreshResult), - &Instance() + result }; } else { // An empty result, except maybe, unusually, the refresh return { { wxString{}, nullptr, refreshResult }, - nullptr + {} }; } } @@ -93,15 +93,10 @@ UIHandle::Result LabelGlyphHandle::Click { auto result = LabelDefaultClickHandle::Click( evt, pProject ); - const auto pCell = evt.pCell; const wxMouseEvent &event = evt.event; - const wxRect &rect = evt.rect; - - mpLT = std::static_pointer_cast(pCell); - mRect = rect; ViewInfo &viewInfo = pProject->GetViewInfo(); - mpLT->HandleGlyphClick(event, rect, viewInfo, &viewInfo.selectedRegion); + mpLT->HandleGlyphClick(event, mRect, viewInfo, &viewInfo.selectedRegion); if (! mpLT->IsAdjustingLabel() ) { diff --git a/src/tracks/labeltrack/ui/LabelGlyphHandle.h b/src/tracks/labeltrack/ui/LabelGlyphHandle.h index 72e12754a..e35d2d87b 100644 --- a/src/tracks/labeltrack/ui/LabelGlyphHandle.h +++ b/src/tracks/labeltrack/ui/LabelGlyphHandle.h @@ -21,15 +21,19 @@ class LabelTrack; class LabelGlyphHandle final : public LabelDefaultClickHandle { - LabelGlyphHandle(); LabelGlyphHandle(const LabelGlyphHandle&) = delete; - LabelGlyphHandle &operator=(const LabelGlyphHandle&) = delete; - static LabelGlyphHandle& Instance(); static HitTestPreview HitPreview(bool hitCenter, unsigned refreshResult); public: + explicit LabelGlyphHandle + (const std::shared_ptr &pLT, const wxRect &rect); + + LabelGlyphHandle &operator=(LabelGlyphHandle&&) = default; + static HitTestResult HitTest - (const wxMouseState &state, const std::shared_ptr &pLT); + (std::weak_ptr &holder, + const wxMouseState &state, + const std::shared_ptr &pLT, const wxRect &rect); virtual ~LabelGlyphHandle(); diff --git a/src/tracks/labeltrack/ui/LabelTextHandle.cpp b/src/tracks/labeltrack/ui/LabelTextHandle.cpp index 03d2ab30d..b6afca1aa 100644 --- a/src/tracks/labeltrack/ui/LabelTextHandle.cpp +++ b/src/tracks/labeltrack/ui/LabelTextHandle.cpp @@ -18,15 +18,11 @@ Paul Licameli split from TrackPanel.cpp #include "../../../ViewInfo.h" #include "../../../images/Cursors.h" -LabelTextHandle::LabelTextHandle() -{ -} - -LabelTextHandle &LabelTextHandle::Instance() -{ - static LabelTextHandle instance; - return instance; -} +LabelTextHandle::LabelTextHandle +( const std::shared_ptr &pLT, int labelNum ) + : mpLT{ pLT } + , mLabelNum{ labelNum } +{} HitTestPreview LabelTextHandle::HitPreview() { @@ -39,13 +35,18 @@ HitTestPreview LabelTextHandle::HitPreview() } HitTestResult LabelTextHandle::HitTest -(const wxMouseState &state, const std::shared_ptr &pLT) +(std::weak_ptr &holder, + const wxMouseState &state, const std::shared_ptr &pLT) { // If Control is down, let the select handle be hit instead + int labelNum; if (!state.ControlDown() && - pLT->OverATextBox(state.m_x, state.m_y) >= 0) + (labelNum = pLT->OverATextBox(state.m_x, state.m_y) ) >= 0) { + auto result = std::make_shared( pLT, labelNum ); + result = AssignUIHandlePtr(holder, result); // There was no cursor change or status message for mousing over a label text box - return { HitPreview(), &Instance() }; + return { HitPreview(), result }; + } return {}; } @@ -57,6 +58,10 @@ LabelTextHandle::~LabelTextHandle() UIHandle::Result LabelTextHandle::Click (const TrackPanelMouseEvent &evt, AudacityProject *pProject) { + auto pLT = mpLT.lock(); + if (!pLT) + return RefreshCode::Cancelled; + auto result = LabelDefaultClickHandle::Click( evt, pProject ); auto &selectionState = pProject->GetSelectionState(); @@ -64,12 +69,9 @@ UIHandle::Result LabelTextHandle::Click mChanger = std::make_unique< SelectionStateChanger >( selectionState, *tracks ); - const auto pCell = evt.pCell; const wxMouseEvent &event = evt.event; ViewInfo &viewInfo = pProject->GetViewInfo(); - auto pLT = std::static_pointer_cast(pCell); - mpLT = pLT; mSelectedRegion = viewInfo.selectedRegion; pLT->HandleTextClick( event, evt.rect, viewInfo, &viewInfo.selectedRegion ); wxASSERT(pLT->IsSelected()); diff --git a/src/tracks/labeltrack/ui/LabelTextHandle.h b/src/tracks/labeltrack/ui/LabelTextHandle.h index eec217872..cd40efb3f 100644 --- a/src/tracks/labeltrack/ui/LabelTextHandle.h +++ b/src/tracks/labeltrack/ui/LabelTextHandle.h @@ -23,19 +23,24 @@ class SelectionStateChanger; class LabelTextHandle final : public LabelDefaultClickHandle { - LabelTextHandle(); LabelTextHandle(const LabelTextHandle&) = delete; - LabelTextHandle &operator=(const LabelTextHandle&) = delete; - static LabelTextHandle& Instance(); static HitTestPreview HitPreview(); public: - static HitTestResult HitTest( - const wxMouseState &state, const std::shared_ptr &pLT); + static HitTestResult HitTest + (std::weak_ptr &holder, + const wxMouseState &state, const std::shared_ptr &pLT); + LabelTextHandle &operator=(LabelTextHandle&&) = default; + + explicit LabelTextHandle + ( const std::shared_ptr &pLT, int labelNum ); virtual ~LabelTextHandle(); + std::shared_ptr GetTrack() const { return mpLT.lock(); } + int GetLabelNum() const { return mLabelNum; } + Result Click (const TrackPanelMouseEvent &event, AudacityProject *pProject) override; @@ -54,6 +59,7 @@ public: private: std::weak_ptr mpLT {}; + int mLabelNum{ -1 }; int mLabelTrackStartXPos { -1 }; int mLabelTrackStartYPos { -1 }; SelectedRegion mSelectedRegion{}; diff --git a/src/tracks/labeltrack/ui/LabelTrackUI.cpp b/src/tracks/labeltrack/ui/LabelTrackUI.cpp index 3e6bcbaf4..bf192020d 100644 --- a/src/tracks/labeltrack/ui/LabelTrackUI.cpp +++ b/src/tracks/labeltrack/ui/LabelTrackUI.cpp @@ -29,12 +29,14 @@ HitTestResult LabelTrack::DetailedHitTest const wxMouseState &state = st.state; // Try label movement handles first - result = LabelGlyphHandle::HitTest(state, Pointer(this)); + result = LabelGlyphHandle::HitTest( + mGlyphHandle, state, Pointer(this), st.rect); auto refresh = result.preview.refreshCode; // kludge if ( !result.handle ) { // Missed glyph, try text box - result = LabelTextHandle::HitTest(state, Pointer(this)); + result = LabelTextHandle::HitTest( + mTextHandle, state, Pointer(this)); result.preview.refreshCode |= refresh; // kludge } diff --git a/src/tracks/playabletrack/notetrack/ui/NoteTrackButtonHandle.cpp b/src/tracks/playabletrack/notetrack/ui/NoteTrackButtonHandle.cpp index 3b5a1e700..15fb91435 100644 --- a/src/tracks/playabletrack/notetrack/ui/NoteTrackButtonHandle.cpp +++ b/src/tracks/playabletrack/notetrack/ui/NoteTrackButtonHandle.cpp @@ -18,22 +18,21 @@ Paul Licameli split from TrackPanel.cpp #include "../../../../RefreshCode.h" #include "../../../../TrackPanel.h" -NoteTrackButtonHandle::NoteTrackButtonHandle() -{ -} +NoteTrackButtonHandle::NoteTrackButtonHandle +( const std::shared_ptr &pTrack, + int channel, const wxRect &rect ) + : mpTrack{ pTrack } + , mChannel{ channel } + , mRect{ rect } +{} NoteTrackButtonHandle::~NoteTrackButtonHandle() { } -NoteTrackButtonHandle &NoteTrackButtonHandle::Instance() -{ - static NoteTrackButtonHandle instance; - return instance; -} - HitTestResult NoteTrackButtonHandle::HitTest - (const wxMouseState &state, const wxRect &rect, + (std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const std::shared_ptr &pTrack) { wxRect midiRect; @@ -42,12 +41,14 @@ HitTestResult NoteTrackButtonHandle::HitTest return {}; if (pTrack->GetKind() == Track::Note && midiRect.Contains(state.m_x, state.m_y)) { - Instance().mpTrack = pTrack; - Instance().mRect = midiRect; - return { - HitTestPreview(), - &Instance() - }; + auto channel = pTrack->FindChannel(midiRect, state.m_x, state.m_y); + auto result = std::make_shared( + pTrack, channel, midiRect ); + result = AssignUIHandlePtr(holder, result); + return { + HitTestPreview(), + result + }; } else return {}; @@ -68,6 +69,10 @@ UIHandle::Result NoteTrackButtonHandle::Drag HitTestPreview NoteTrackButtonHandle::Preview (const TrackPanelMouseState &, const AudacityProject *) { + // auto pTrack = pProject->GetTracks()->Lock(mpTrack); + auto pTrack = mpTrack.lock(); + if ( !pTrack ) + return {}; // No special message or cursor return {}; } diff --git a/src/tracks/playabletrack/notetrack/ui/NoteTrackButtonHandle.h b/src/tracks/playabletrack/notetrack/ui/NoteTrackButtonHandle.h index e1abe01c6..7503c8633 100644 --- a/src/tracks/playabletrack/notetrack/ui/NoteTrackButtonHandle.h +++ b/src/tracks/playabletrack/notetrack/ui/NoteTrackButtonHandle.h @@ -24,15 +24,24 @@ struct HitTestResult; class NoteTrackButtonHandle : public UIHandle { NoteTrackButtonHandle(const NoteTrackButtonHandle&); - NoteTrackButtonHandle &operator=(const NoteTrackButtonHandle&); NoteTrackButtonHandle(); - virtual ~NoteTrackButtonHandle(); static NoteTrackButtonHandle& Instance(); public: + explicit NoteTrackButtonHandle + ( const std::shared_ptr &pTrack, + int channel, const wxRect &rect ); + + NoteTrackButtonHandle &operator=(const NoteTrackButtonHandle&) = default; + + virtual ~NoteTrackButtonHandle(); + static HitTestResult HitTest - (const wxMouseState &state, const wxRect &rect, - const std::shared_ptr &pTrack); + (std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, + const std::shared_ptr &pTrack); + + int GetChannel() const { return mChannel; } protected: Result Click @@ -52,6 +61,7 @@ protected: Result Cancel(AudacityProject *pProject) override; std::weak_ptr mpTrack; + int mChannel{ -1 }; wxRect mRect{}; }; diff --git a/src/tracks/playabletrack/notetrack/ui/NoteTrackControls.cpp b/src/tracks/playabletrack/notetrack/ui/NoteTrackControls.cpp index 7ce5b7963..bc19df72a 100644 --- a/src/tracks/playabletrack/notetrack/ui/NoteTrackControls.cpp +++ b/src/tracks/playabletrack/notetrack/ui/NoteTrackControls.cpp @@ -40,21 +40,19 @@ HitTestResult NoteTrackControls::HitTest auto track = std::static_pointer_cast(FindTrack()); if (track && track->GetKind() == Track::Note) { HitTestResult result; - if (NULL != - (result = MuteButtonHandle::HitTest - (state, rect, pProject, track)).handle) + if (NULL != (result = MuteButtonHandle::HitTest( + mMuteHandle, state, rect, pProject, track)).handle) return result; - if (NULL != - (result = SoloButtonHandle::HitTest - (state, rect, pProject, track)).handle) + if (NULL != (result = SoloButtonHandle::HitTest( + mSoloHandle, state, rect, pProject, track)).handle) return result; #ifdef EXPERIMENTAL_MIDI_OUT - if (NULL != (result = - VelocitySliderHandle::HitTest(state, rect, pProject, track)).handle) + if (NULL != (result = VelocitySliderHandle::HitTest( + mVelocityHandle, state, rect, pProject, track)).handle) return result; - if (NULL != (result = - NoteTrackButtonHandle::HitTest(state, rect, track)).handle) + if (NULL != (result = NoteTrackButtonHandle::HitTest( + mClickHandle, state, rect, track)).handle) return result; #endif } diff --git a/src/tracks/playabletrack/notetrack/ui/NoteTrackControls.h b/src/tracks/playabletrack/notetrack/ui/NoteTrackControls.h index 84ed1846a..9acb56102 100644 --- a/src/tracks/playabletrack/notetrack/ui/NoteTrackControls.h +++ b/src/tracks/playabletrack/notetrack/ui/NoteTrackControls.h @@ -12,12 +12,23 @@ Paul Licameli split from TrackPanel.cpp #define __AUDACITY_NOTE_TRACK_CONTROLS__ #include "../../../ui/TrackControls.h" +#include "../../../../MemoryX.h" +class MuteButtonHandle; +class SoloButtonHandle; +class NoteTrackButtonHandle; +class VelocitySliderHandle; +/////////////////////////////////////////////////////////////////////////////// class NoteTrackControls : public TrackControls { NoteTrackControls(const NoteTrackControls&) = delete; NoteTrackControls &operator=(const NoteTrackControls&) = delete; + std::weak_ptr mMuteHandle; + std::weak_ptr mSoloHandle; + std::weak_ptr mClickHandle; + std::weak_ptr mVelocityHandle; + public: explicit NoteTrackControls( std::shared_ptr pTrack ) diff --git a/src/tracks/playabletrack/notetrack/ui/NoteTrackSliderHandles.cpp b/src/tracks/playabletrack/notetrack/ui/NoteTrackSliderHandles.cpp index 9c97c119b..6718f55f8 100644 --- a/src/tracks/playabletrack/notetrack/ui/NoteTrackSliderHandles.cpp +++ b/src/tracks/playabletrack/notetrack/ui/NoteTrackSliderHandles.cpp @@ -21,21 +21,16 @@ #include "../../../../UndoManager.h" #include "../../../../NoteTrack.h" -VelocitySliderHandle::VelocitySliderHandle() -: SliderHandle() -{ -} +VelocitySliderHandle::VelocitySliderHandle +( SliderFn sliderFn, const wxRect &rect, + const std::shared_ptr &pTrack ) + : SliderHandle{ sliderFn, rect, pTrack } +{} VelocitySliderHandle::~VelocitySliderHandle() { } -VelocitySliderHandle &VelocitySliderHandle::Instance() -{ - static VelocitySliderHandle instance; - return instance; -} - std::shared_ptr VelocitySliderHandle::GetNoteTrack() { return std::static_pointer_cast(mpTrack.lock()); @@ -69,7 +64,8 @@ UIHandle::Result VelocitySliderHandle::CommitChanges HitTestResult VelocitySliderHandle::HitTest -(const wxMouseState &state, const wxRect &rect, +(std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr &pTrack) { if (!state.ButtonIsDown(wxMOUSE_BTN_LEFT)) @@ -80,16 +76,17 @@ HitTestResult VelocitySliderHandle::HitTest if ( TrackInfo::HideTopItem( rect, sliderRect, kTrackInfoSliderAllowance ) ) return {}; if (sliderRect.Contains(state.m_x, state.m_y)) { - Instance().mSliderFn = + auto sliderFn = []( AudacityProject *pProject, const wxRect &sliderRect, Track *pTrack ) { return TrackInfo::VelocitySlider (sliderRect, static_cast( pTrack ), true, const_cast(pProject->GetTrackPanel())); }; - Instance().mRect = sliderRect; - Instance().mpTrack = pTrack; + auto result = std::make_shared( + sliderFn, sliderRect, pTrack ); + result = AssignUIHandlePtr(holder, result); - return { HitPreview(), &Instance() }; + return { HitPreview(), result }; } else return {}; diff --git a/src/tracks/playabletrack/notetrack/ui/NoteTrackSliderHandles.h b/src/tracks/playabletrack/notetrack/ui/NoteTrackSliderHandles.h index 756322bbc..d2303a854 100644 --- a/src/tracks/playabletrack/notetrack/ui/NoteTrackSliderHandles.h +++ b/src/tracks/playabletrack/notetrack/ui/NoteTrackSliderHandles.h @@ -25,14 +25,18 @@ struct HitTestResult; class VelocitySliderHandle final : public SliderHandle { VelocitySliderHandle(const VelocitySliderHandle&) = delete; - VelocitySliderHandle &operator=(const VelocitySliderHandle&) = delete; - - VelocitySliderHandle(); - virtual ~VelocitySliderHandle(); - static VelocitySliderHandle& Instance(); std::shared_ptr GetNoteTrack(); +public: + explicit VelocitySliderHandle + ( SliderFn sliderFn, const wxRect &rect, + const std::shared_ptr &pTrack ); + + VelocitySliderHandle &operator=(const VelocitySliderHandle&) = default; + + virtual ~VelocitySliderHandle(); + protected: float GetValue() override; Result SetValue @@ -44,7 +48,8 @@ protected: public: static HitTestResult HitTest - (const wxMouseState &state, const wxRect &rect, + (std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr &pTrack); }; diff --git a/src/tracks/playabletrack/notetrack/ui/NoteTrackUI.cpp b/src/tracks/playabletrack/notetrack/ui/NoteTrackUI.cpp index e3bcae7db..d292b4a23 100644 --- a/src/tracks/playabletrack/notetrack/ui/NoteTrackUI.cpp +++ b/src/tracks/playabletrack/notetrack/ui/NoteTrackUI.cpp @@ -29,8 +29,8 @@ HitTestResult NoteTrack::DetailedHitTest // Eligible for stretch? HitTestResult result; #ifdef USE_MIDI - StretchHandle::StretchState stretchState; - result = StretchHandle::HitTest( state, pProject, Pointer(this), stretchState ); + result = StretchHandle::HitTest( + mStretchHandle, state, pProject, Pointer(this) ); #endif return result; diff --git a/src/tracks/playabletrack/notetrack/ui/NoteTrackVRulerControls.cpp b/src/tracks/playabletrack/notetrack/ui/NoteTrackVRulerControls.cpp index 4eae4a6be..dfffb682d 100644 --- a/src/tracks/playabletrack/notetrack/ui/NoteTrackVRulerControls.cpp +++ b/src/tracks/playabletrack/notetrack/ui/NoteTrackVRulerControls.cpp @@ -31,7 +31,10 @@ HitTestResult NoteTrackVRulerControls::HitTest (const TrackPanelMouseState &st, const AudacityProject *) { - return NoteTrackVZoomHandle::HitTest(st.state); + UIHandlePtr result; + auto track = std::static_pointer_cast(FindTrack()); + return NoteTrackVZoomHandle::HitTest( + mVZoomHandle, st.state, track, st.rect); } unsigned NoteTrackVRulerControls::HandleWheelRotation diff --git a/src/tracks/playabletrack/notetrack/ui/NoteTrackVRulerControls.h b/src/tracks/playabletrack/notetrack/ui/NoteTrackVRulerControls.h index 06f6aa35d..0eaf286f4 100644 --- a/src/tracks/playabletrack/notetrack/ui/NoteTrackVRulerControls.h +++ b/src/tracks/playabletrack/notetrack/ui/NoteTrackVRulerControls.h @@ -13,6 +13,8 @@ Paul Licameli split from TrackPanel.cpp #include "../../../ui/TrackVRulerControls.h" +class NoteTrackVZoomHandle; + class NoteTrackVRulerControls final : public TrackVRulerControls { NoteTrackVRulerControls(const NoteTrackVRulerControls&) = delete; @@ -31,6 +33,9 @@ public: unsigned HandleWheelRotation (const TrackPanelMouseEvent &event, AudacityProject *pProject) override; + +private: + std::weak_ptr mVZoomHandle; }; #endif diff --git a/src/tracks/playabletrack/notetrack/ui/NoteTrackVZoomHandle.cpp b/src/tracks/playabletrack/notetrack/ui/NoteTrackVZoomHandle.cpp index 8b22e3e65..7a80b58e2 100644 --- a/src/tracks/playabletrack/notetrack/ui/NoteTrackVZoomHandle.cpp +++ b/src/tracks/playabletrack/notetrack/ui/NoteTrackVZoomHandle.cpp @@ -34,16 +34,11 @@ namespace /////////////////////////////////////////////////////////////////////////////// -NoteTrackVZoomHandle::NoteTrackVZoomHandle() - : mZoomStart(0), mZoomEnd(0), mRect() -{ -} - -NoteTrackVZoomHandle &NoteTrackVZoomHandle::Instance() -{ - static NoteTrackVZoomHandle instance; - return instance; -} +NoteTrackVZoomHandle::NoteTrackVZoomHandle +(const std::shared_ptr &pTrack, const wxRect &rect, int y) + : mZoomStart(y), mZoomEnd(y), mRect(rect) + , mpTrack{ pTrack } +{} HitTestPreview NoteTrackVZoomHandle::HitPreview(const wxMouseState &state) { @@ -57,9 +52,18 @@ HitTestPreview NoteTrackVZoomHandle::HitPreview(const wxMouseState &state) }; } -HitTestResult NoteTrackVZoomHandle::HitTest(const wxMouseState &state) +HitTestResult NoteTrackVZoomHandle::HitTest +(std::weak_ptr &holder, + const wxMouseState &state, + const std::shared_ptr &pTrack, const wxRect &rect) { - return HitTestResult(HitPreview(state), &Instance()); + if (pTrack) { + auto result = std::make_shared( + pTrack, rect, state.m_y); + result = AssignUIHandlePtr(holder, result); + return HitTestResult(HitPreview(state), result); + } + return {}; } NoteTrackVZoomHandle::~NoteTrackVZoomHandle() @@ -67,16 +71,8 @@ NoteTrackVZoomHandle::~NoteTrackVZoomHandle() } UIHandle::Result NoteTrackVZoomHandle::Click -(const TrackPanelMouseEvent &evt, AudacityProject *pProject) +(const TrackPanelMouseEvent &, AudacityProject *) { - mpTrack = std::static_pointer_cast( - static_cast(evt.pCell.get())->FindTrack() ); - mRect = evt.rect; - - const wxMouseEvent &event = evt.event; - mZoomStart = event.m_y; - mZoomEnd = event.m_y; - // change note track to zoom like audio track // mpTrack->StartVScroll(); diff --git a/src/tracks/playabletrack/notetrack/ui/NoteTrackVZoomHandle.h b/src/tracks/playabletrack/notetrack/ui/NoteTrackVZoomHandle.h index 0f33292db..ee06d178e 100644 --- a/src/tracks/playabletrack/notetrack/ui/NoteTrackVZoomHandle.h +++ b/src/tracks/playabletrack/notetrack/ui/NoteTrackVZoomHandle.h @@ -21,17 +21,24 @@ class NoteTrack; class NoteTrackVZoomHandle : public UIHandle { - NoteTrackVZoomHandle(); NoteTrackVZoomHandle(const NoteTrackVZoomHandle&); - NoteTrackVZoomHandle &operator=(const NoteTrackVZoomHandle&); - static NoteTrackVZoomHandle& Instance(); static HitTestPreview HitPreview(const wxMouseState &state); public: - static HitTestResult HitTest(const wxMouseState &state); + explicit NoteTrackVZoomHandle + (const std::shared_ptr &pTrack, const wxRect &rect, int y); + + NoteTrackVZoomHandle &operator=(const NoteTrackVZoomHandle&) = default; + + static HitTestResult HitTest + (std::weak_ptr &holder, + const wxMouseState &state, + const std::shared_ptr &pTrack, const wxRect &rect); virtual ~NoteTrackVZoomHandle(); + std::shared_ptr GetTrack() const { return mpTrack.lock(); } + Result Click (const TrackPanelMouseEvent &event, AudacityProject *pProject) override; diff --git a/src/tracks/playabletrack/notetrack/ui/StretchHandle.cpp b/src/tracks/playabletrack/notetrack/ui/StretchHandle.cpp index edb5835ea..f333ff19e 100644 --- a/src/tracks/playabletrack/notetrack/ui/StretchHandle.cpp +++ b/src/tracks/playabletrack/notetrack/ui/StretchHandle.cpp @@ -24,15 +24,11 @@ Paul Licameli split from TrackPanel.cpp #include -StretchHandle::StretchHandle() -{ -} - -StretchHandle &StretchHandle::Instance() -{ - static StretchHandle instance; - return instance; -} +StretchHandle::StretchHandle +( const std::shared_ptr &pTrack, const StretchState &stretchState ) + : mpTrack{ pTrack } + , mStretchState{ stretchState } +{} HitTestPreview StretchHandle::HitPreview( StretchEnum stretchMode, bool unsafe ) { @@ -68,9 +64,11 @@ HitTestPreview StretchHandle::HitPreview( StretchEnum stretchMode, bool unsafe ) } HitTestResult StretchHandle::HitTest - ( const TrackPanelMouseState &st, const AudacityProject *pProject, - const std::shared_ptr &pTrack, StretchState &stretchState) +(std::weak_ptr &holder, + const TrackPanelMouseState &st, const AudacityProject *pProject, + const std::shared_ptr &pTrack) { + StretchState stretchState; const wxMouseState &state = st.state; // later, we may want a different policy, but for now, stretch is @@ -140,9 +138,11 @@ HitTestResult StretchHandle::HitTest stretchState.mBeatCenter.second - stretchState.mBeat0.second; } + auto result = std::make_shared( pTrack, stretchState ); + result = AssignUIHandlePtr(holder, result); return { HitPreview( stretchState.mMode, unsafe ), - &Instance() + result }; } @@ -167,14 +167,8 @@ UIHandle::Result StretchHandle::Click mLeftEdge = evt.rect.GetLeft(); - mpTrack = std::static_pointer_cast(evt.pCell); ViewInfo &viewInfo = pProject->GetViewInfo(); - // We must have hit if we got here, but repeat some - // calculations that set members - TrackPanelMouseState tpmState{ evt.event, evt.rect, evt.pCell }; - HitTest( tpmState, pProject, mpTrack, mStretchState ); - viewInfo.selectedRegion.setTimes ( mStretchState.mBeat0.first, mStretchState.mBeat1.first ); diff --git a/src/tracks/playabletrack/notetrack/ui/StretchHandle.h b/src/tracks/playabletrack/notetrack/ui/StretchHandle.h index cc3e1c843..59f14079e 100644 --- a/src/tracks/playabletrack/notetrack/ui/StretchHandle.h +++ b/src/tracks/playabletrack/notetrack/ui/StretchHandle.h @@ -55,16 +55,20 @@ public: }; private: - StretchHandle(); StretchHandle(const StretchHandle&); - StretchHandle &operator=(const StretchHandle&); - static StretchHandle& Instance(); static HitTestPreview HitPreview(StretchEnum stretchMode, bool unsafe); public: + explicit StretchHandle + ( const std::shared_ptr &pTrack, + const StretchState &stretchState ); + + StretchHandle &operator=(const StretchHandle&) = default; + static HitTestResult HitTest - ( const TrackPanelMouseState &state, const AudacityProject *pProject, - const std::shared_ptr &pTrack, StretchState &stretchState ); + (std::weak_ptr &holder, + const TrackPanelMouseState &state, const AudacityProject *pProject, + const std::shared_ptr &pTrack ); virtual ~StretchHandle(); diff --git a/src/tracks/playabletrack/ui/PlayableTrackButtonHandles.cpp b/src/tracks/playabletrack/ui/PlayableTrackButtonHandles.cpp index 111b58541..8c3600769 100644 --- a/src/tracks/playabletrack/ui/PlayableTrackButtonHandles.cpp +++ b/src/tracks/playabletrack/ui/PlayableTrackButtonHandles.cpp @@ -18,21 +18,15 @@ Paul Licameli split from TrackPanel.cpp #include "../../../TrackPanel.h" #include "../../../TrackPanelMouseEvent.h" -MuteButtonHandle::MuteButtonHandle() - : ButtonHandle{ TrackPanel::IsMuting } -{ -} +MuteButtonHandle::MuteButtonHandle +( const std::shared_ptr &pTrack, const wxRect &rect ) + : ButtonHandle{ pTrack, rect, TrackPanel::IsMuting } +{} MuteButtonHandle::~MuteButtonHandle() { } -MuteButtonHandle &MuteButtonHandle::Instance() -{ - static MuteButtonHandle instance; - return instance; -} - UIHandle::Result MuteButtonHandle::CommitChanges (const wxMouseEvent &event, AudacityProject *pProject, wxWindow *) { @@ -44,7 +38,8 @@ UIHandle::Result MuteButtonHandle::CommitChanges } HitTestResult MuteButtonHandle::HitTest -(const wxMouseState &state, const wxRect &rect, +(std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr &pTrack) { wxRect buttonRect; @@ -55,10 +50,11 @@ HitTestResult MuteButtonHandle::HitTest return {}; if ( pTrack && buttonRect.Contains(state.m_x, state.m_y) ) { - Instance().mRect = buttonRect; + auto result = std::make_shared(pTrack, buttonRect); + result = AssignUIHandlePtr(holder, result); return { HitPreview(), - &Instance() + result }; } else @@ -67,21 +63,15 @@ HitTestResult MuteButtonHandle::HitTest //////////////////////////////////////////////////////////////////////////////// -SoloButtonHandle::SoloButtonHandle() - : ButtonHandle{ TrackPanel::IsSoloing } -{ -} +SoloButtonHandle::SoloButtonHandle +( const std::shared_ptr &pTrack, const wxRect &rect ) + : ButtonHandle{ pTrack, rect, TrackPanel::IsSoloing } +{} SoloButtonHandle::~SoloButtonHandle() { } -SoloButtonHandle &SoloButtonHandle::Instance() -{ - static SoloButtonHandle instance; - return instance; -} - UIHandle::Result SoloButtonHandle::CommitChanges (const wxMouseEvent &event, AudacityProject *pProject, wxWindow *pParent) { @@ -93,7 +83,8 @@ UIHandle::Result SoloButtonHandle::CommitChanges } HitTestResult SoloButtonHandle::HitTest -(const wxMouseState &state, const wxRect &rect, +(std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr &pTrack) { wxRect buttonRect; @@ -105,10 +96,11 @@ HitTestResult SoloButtonHandle::HitTest return {}; if ( pTrack && buttonRect.Contains(state.m_x, state.m_y) ) { - Instance().mRect = buttonRect; + auto result = std::make_shared( pTrack, buttonRect ); + result = AssignUIHandlePtr(holder, result); return HitTestResult( HitPreview(), - &Instance() + result ); } else diff --git a/src/tracks/playabletrack/ui/PlayableTrackButtonHandles.h b/src/tracks/playabletrack/ui/PlayableTrackButtonHandles.h index 17a376cde..f2b8b5792 100644 --- a/src/tracks/playabletrack/ui/PlayableTrackButtonHandles.h +++ b/src/tracks/playabletrack/ui/PlayableTrackButtonHandles.h @@ -12,18 +12,23 @@ Paul Licameli split from TrackPanel.cpp #define __AUDACITY_PLAYABLE_TRACK_BUTTON_HANDLES__ #include "../../ui/ButtonHandle.h" +#include "../../../TrackPanel.h" struct HitTestResult; + class wxMouseState; class MuteButtonHandle final : public ButtonHandle { MuteButtonHandle(const MuteButtonHandle&) = delete; - MuteButtonHandle &operator=(const MuteButtonHandle&) = delete; - MuteButtonHandle(); +public: + explicit MuteButtonHandle + ( const std::shared_ptr &pTrack, const wxRect &rect ); + + MuteButtonHandle &operator=(const MuteButtonHandle&) = default; + virtual ~MuteButtonHandle(); - static MuteButtonHandle& Instance(); protected: Result CommitChanges @@ -34,7 +39,8 @@ protected: public: static HitTestResult HitTest - (const wxMouseState &state, const wxRect &rect, + (std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr &pTrack); }; @@ -43,11 +49,14 @@ public: class SoloButtonHandle final : public ButtonHandle { SoloButtonHandle(const SoloButtonHandle&) = delete; - SoloButtonHandle &operator=(const SoloButtonHandle&) = delete; - SoloButtonHandle(); +public: + explicit SoloButtonHandle + ( const std::shared_ptr &pTrack, const wxRect &rect ); + + SoloButtonHandle &operator=(const SoloButtonHandle&) = default; + virtual ~SoloButtonHandle(); - static SoloButtonHandle& Instance(); protected: Result CommitChanges @@ -58,7 +67,8 @@ protected: public: static HitTestResult HitTest - (const wxMouseState &state, const wxRect &rect, + (std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr &pTrack); }; diff --git a/src/tracks/playabletrack/wavetrack/ui/CutlineHandle.cpp b/src/tracks/playabletrack/wavetrack/ui/CutlineHandle.cpp index 43b57596a..f27253ca6 100644 --- a/src/tracks/playabletrack/wavetrack/ui/CutlineHandle.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/CutlineHandle.cpp @@ -22,15 +22,11 @@ Paul Licameli split from TrackPanel.cpp #include "../../../../WaveTrackLocation.h" #include "../../../../../images/Cursors.h" -CutlineHandle::CutlineHandle() -{ -} - -CutlineHandle &CutlineHandle::Instance() -{ - static CutlineHandle instance; - return instance; -} +CutlineHandle::CutlineHandle +( const std::shared_ptr &pTrack, WaveTrackLocation location ) + : mpTrack{ pTrack } + , mLocation{ location } +{} HitTestPreview CutlineHandle::HitPreview(bool cutline, bool unsafe) { @@ -47,10 +43,11 @@ HitTestPreview CutlineHandle::HitPreview(bool cutline, bool unsafe) }; } -HitTestResult CutlineHandle::HitAnywhere(const AudacityProject *pProject, bool cutline) +HitTestResult CutlineHandle::HitAnywhere +(const AudacityProject *pProject, bool cutline, UIHandlePtr ptr) { const bool unsafe = pProject->IsAudioActive(); - return { HitPreview(cutline, unsafe), &Instance() }; + return { HitPreview(cutline, unsafe), ptr }; } namespace @@ -71,7 +68,7 @@ namespace bool IsOverCutline (const ViewInfo &viewInfo, WaveTrack * track, const wxRect &rect, const wxMouseState &state, - WaveTrackLocation *pCapturedTrackLocation) + WaveTrackLocation *pmLocation) { for (auto loc: track->GetCachedLocations()) { @@ -90,8 +87,8 @@ namespace } if (locRect.Contains(state.m_x, state.m_y)) { - if (pCapturedTrackLocation) - *pCapturedTrackLocation = loc; + if (pmLocation) + *pmLocation = loc; return true; } } @@ -102,7 +99,8 @@ namespace } HitTestResult CutlineHandle::HitTest -(const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, +(std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr &pTrack) { const ViewInfo &viewInfo = pProject->GetViewInfo(); @@ -116,7 +114,9 @@ HitTestResult CutlineHandle::HitTest if (!IsOverCutline(viewInfo, wavetrack, rect, state, &location)) return {}; - return HitAnywhere(pProject, location.typ == WaveTrackLocation::locationCutLine); + auto result = std::make_shared( pTrack, location ); + result = AssignUIHandlePtr( holder, result ); + return HitAnywhere(pProject, location.typ == WaveTrackLocation::locationCutLine, result); } CutlineHandle::~CutlineHandle() @@ -133,7 +133,6 @@ UIHandle::Result CutlineHandle::Click const wxMouseEvent &event = evt.event; ViewInfo &viewInfo = pProject->GetViewInfo(); - const auto pTrack = static_cast(evt.pCell.get()); // Can affect the track by merging clips, expanding a cutline, or // deleting a cutline. @@ -141,24 +140,17 @@ UIHandle::Result CutlineHandle::Click /// Someone has just clicked the mouse. What do we do? - WaveTrackLocation capturedTrackLocation; - - WaveTrack *wavetrack = static_cast(pTrack); - if (!IsOverCutline(viewInfo, wavetrack, evt.rect, event, &capturedTrackLocation)) - return Cancelled; - mbCutline = (capturedTrackLocation.typ == WaveTrackLocation::locationCutLine); - // FIXME: Disable this and return true when CutLines aren't showing? // (Don't use gPrefs-> for the fix as registry access is slow). // Cutline data changed on either branch, so refresh the track display. UIHandle::Result result = RefreshCell; // Assume linked track is wave or null - const auto linked = static_cast(wavetrack->GetLink()); + const auto linked = static_cast(mpTrack->GetLink()); if (event.LeftDown()) { - if (capturedTrackLocation.typ == WaveTrackLocation::locationCutLine) + if (mLocation.typ == WaveTrackLocation::locationCutLine) { mOperation = Expand; mStartTime = viewInfo.selectedRegion.t0(); @@ -167,18 +159,18 @@ UIHandle::Result CutlineHandle::Click // When user presses left button on cut line, expand the line again double cutlineStart = 0, cutlineEnd = 0; - wavetrack->ExpandCutLine(capturedTrackLocation.pos, &cutlineStart, &cutlineEnd); + mpTrack->ExpandCutLine(mLocation.pos, &cutlineStart, &cutlineEnd); if (linked) // Expand the cutline in the opposite channel if it is present. - linked->ExpandCutLine(capturedTrackLocation.pos); + linked->ExpandCutLine(mLocation.pos); viewInfo.selectedRegion.setTimes(cutlineStart, cutlineEnd); result |= UpdateSelection; } - else if (capturedTrackLocation.typ == WaveTrackLocation::locationMergePoint) { - const double pos = capturedTrackLocation.pos; - wavetrack->MergeClips(capturedTrackLocation.clipidx1, capturedTrackLocation.clipidx2); + else if (mLocation.typ == WaveTrackLocation::locationMergePoint) { + const double pos = mLocation.pos; + mpTrack->MergeClips(mLocation.clipidx1, mLocation.clipidx2); if (linked) { // Don't assume correspondence of merge points across channels! @@ -194,10 +186,10 @@ UIHandle::Result CutlineHandle::Click } else if (event.RightDown()) { - bool removed = wavetrack->RemoveCutLine(capturedTrackLocation.pos); + bool removed = mpTrack->RemoveCutLine(mLocation.pos); if (linked) - removed = linked->RemoveCutLine(capturedTrackLocation.pos) || removed; + removed = linked->RemoveCutLine(mLocation.pos) || removed; if (!removed) // Nothing happened, make no Undo item @@ -221,7 +213,8 @@ HitTestPreview CutlineHandle::Preview (const TrackPanelMouseState &, const AudacityProject *pProject) { const bool unsafe = pProject->IsAudioActive(); - return HitPreview( mbCutline, unsafe ); + auto bCutline = (mLocation.typ == WaveTrackLocation::locationCutLine); + return HitPreview( bCutline, unsafe ); } UIHandle::Result CutlineHandle::Release diff --git a/src/tracks/playabletrack/wavetrack/ui/CutlineHandle.h b/src/tracks/playabletrack/wavetrack/ui/CutlineHandle.h index 3550cc2b6..daf8d9112 100644 --- a/src/tracks/playabletrack/wavetrack/ui/CutlineHandle.h +++ b/src/tracks/playabletrack/wavetrack/ui/CutlineHandle.h @@ -11,30 +11,43 @@ Paul Licameli #ifndef __AUDACITY_CUTLINE_HANDLE__ #define __AUDACITY_CUTLINE_HANDLE__ +#include "../../../../MemoryX.h" #include "../../../../UIHandle.h" #include "../../../../MemoryX.h" +#include "../../../../WaveTrackLocation.h" class wxMouseEvent; class wxMouseState; struct HitTestResult; class WaveTrack; +using UIHandlePtr = std::shared_ptr; + class CutlineHandle final : public UIHandle { - CutlineHandle(); CutlineHandle(const CutlineHandle&) = delete; - CutlineHandle &operator=(const CutlineHandle&) = delete; - static CutlineHandle& Instance(); static HitTestPreview HitPreview(bool cutline, bool unsafe); public: - static HitTestResult HitAnywhere(const AudacityProject *pProject, bool cutline); + explicit CutlineHandle + ( const std::shared_ptr &pTrack, + WaveTrackLocation location ); + + CutlineHandle &operator=(const CutlineHandle&) = default; + + static HitTestResult HitAnywhere + (const AudacityProject *pProject, bool cutline, UIHandlePtr ptr); static HitTestResult HitTest - (const wxMouseState &state, const wxRect &rect, - const AudacityProject *pProject, const std::shared_ptr &pTrack); + (std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, + const AudacityProject *pProject, + const std::shared_ptr &pTrack); virtual ~CutlineHandle(); + const WaveTrackLocation &GetLocation() { return mLocation; } + std::shared_ptr GetTrack() { return mpTrack; } + Result Click (const TrackPanelMouseEvent &event, AudacityProject *pProject) override; @@ -57,7 +70,8 @@ private: enum Operation { Merge, Expand, Remove }; Operation mOperation{ Merge }; double mStartTime{}, mEndTime{}; - bool mbCutline{}; + WaveTrackLocation mLocation {}; + std::shared_ptr mpTrack{}; }; #endif diff --git a/src/tracks/playabletrack/wavetrack/ui/SampleHandle.cpp b/src/tracks/playabletrack/wavetrack/ui/SampleHandle.cpp index 3e12c816c..68e47febe 100644 --- a/src/tracks/playabletrack/wavetrack/ui/SampleHandle.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/SampleHandle.cpp @@ -34,15 +34,9 @@ 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; -} +SampleHandle::SampleHandle( const std::shared_ptr &pTrack ) + : mClickedTrack{ pTrack } +{} HitTestPreview SampleHandle::HitPreview (const wxMouseState &state, const AudacityProject *pProject, bool unsafe) @@ -64,10 +58,14 @@ HitTestPreview SampleHandle::HitPreview } HitTestResult SampleHandle::HitAnywhere -(const wxMouseState &state, const AudacityProject *pProject) +(std::weak_ptr &holder, + const wxMouseState &state, const AudacityProject *pProject, + const std::shared_ptr &pTrack) { + auto result = std::make_shared( pTrack ); + result = AssignUIHandlePtr(holder, result); const bool unsafe = pProject->IsAudioActive(); - return { HitPreview(state, pProject, unsafe), &Instance() }; + return { HitPreview(state, pProject, unsafe), result }; } namespace { @@ -100,16 +98,12 @@ namespace { } HitTestResult SampleHandle::HitTest -(const wxMouseState &state, const wxRect &rect, +(std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr &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 = pTrack.get(); const int displayType = wavetrack->GetDisplay(); @@ -155,7 +149,7 @@ HitTestResult SampleHandle::HitTest if (abs(yValue - yMouse) >= yTolerance) return {}; - return HitAnywhere(state, pProject); + return HitAnywhere(holder, state, pProject, pTrack); } SampleHandle::~SampleHandle() @@ -209,15 +203,14 @@ UIHandle::Result SampleHandle::Click const wxMouseEvent &event = evt.event; const wxRect &rect = evt.rect; const ViewInfo &viewInfo = pProject->GetViewInfo(); - const auto pTrack = std::static_pointer_cast(evt.pCell); + const auto pTrack = mClickedTrack.get(); /// Someone has just clicked the mouse. What do we do? if (!IsSampleEditingPossible( - event, rect, viewInfo, pTrack.get(), rect.width)) + event, rect, viewInfo, pTrack, rect.width)) return Cancelled; /// We're in a track view and zoomed enough to see the samples. - mClickedTrack = pTrack; mRect = rect; //If we are still around, we are drawing in earnest. Set some member data structures up: diff --git a/src/tracks/playabletrack/wavetrack/ui/SampleHandle.h b/src/tracks/playabletrack/wavetrack/ui/SampleHandle.h index 4746f1590..08458ca1c 100644 --- a/src/tracks/playabletrack/wavetrack/ui/SampleHandle.h +++ b/src/tracks/playabletrack/wavetrack/ui/SampleHandle.h @@ -26,22 +26,28 @@ class WaveTrack; class SampleHandle final : public UIHandle { - SampleHandle(); SampleHandle(const SampleHandle&) = delete; - SampleHandle &operator=(const SampleHandle&) = delete; - static SampleHandle& Instance(); static HitTestPreview HitPreview (const wxMouseState &state, const AudacityProject *pProject, bool unsafe); public: + explicit SampleHandle( const std::shared_ptr &pTrack ); + + SampleHandle &operator=(const SampleHandle&) = default; + static HitTestResult HitAnywhere - (const wxMouseState &state, const AudacityProject *pProject); + (std::weak_ptr &holder, + const wxMouseState &state, const AudacityProject *pProject, + const std::shared_ptr &pTrack); static HitTestResult HitTest - (const wxMouseState &state, const wxRect &rect, + (std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr &pTrack); virtual ~SampleHandle(); + std::shared_ptr GetTrack() const { return mClickedTrack; } + Result Click (const TrackPanelMouseEvent &event, AudacityProject *pProject) override; diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp index f293bab76..bd71f9435 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp @@ -63,22 +63,20 @@ HitTestResult WaveTrackControls::HitTest auto track = FindTrack(); if (track && track->GetKind() == Track::Wave) { HitTestResult result; - if (NULL != - (result = MuteButtonHandle::HitTest - (state, rect, pProject, track)).handle) + if (NULL != (result = MuteButtonHandle::HitTest( + mMuteHandle, state, rect, pProject, track)).handle) return result; - if (NULL != - (result = SoloButtonHandle::HitTest - (state, rect, pProject, track)).handle) + if (NULL != (result = SoloButtonHandle::HitTest( + mSoloHandle, state, rect, pProject, track)).handle) return result; - if (NULL != (result = - GainSliderHandle::HitTest(state, rect, pProject, track)).handle) + if (NULL != (result = GainSliderHandle::HitTest( + mGainHandle, state, rect, pProject, track)).handle) return result; - if (NULL != (result = - PanSliderHandle::HitTest(state, rect, pProject, track)).handle) + if (NULL != (result = PanSliderHandle::HitTest( + mPanHandle, state, rect, pProject, track)).handle) return result; } } diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.h b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.h index 1c4939da0..84cefabca 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.h +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.h @@ -13,6 +13,11 @@ Paul Licameli split from TrackPanel.cpp #include "../../../ui/TrackControls.h" +class MuteButtonHandle; +class SoloButtonHandle; +class GainSliderHandle; +class PanSliderHandle; + class WaveTrackControls final : public TrackControls { WaveTrackControls(const WaveTrackControls&) = delete; @@ -29,6 +34,12 @@ public: const AudacityProject *pProject) override; PopupMenuTable *GetMenuExtension(Track *pTrack) override; + +private: + std::weak_ptr mMuteHandle; + std::weak_ptr mSoloHandle; + std::weak_ptr mGainHandle; + std::weak_ptr mPanHandle; }; #endif diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackSliderHandles.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackSliderHandles.cpp index 26509229c..8256602b9 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackSliderHandles.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackSliderHandles.cpp @@ -19,21 +19,15 @@ Paul Licameli split from TrackPanel.cpp #include "../../../../UndoManager.h" #include "../../../../WaveTrack.h" -GainSliderHandle::GainSliderHandle() - : SliderHandle() -{ -} +GainSliderHandle::GainSliderHandle +( SliderFn sliderFn, const wxRect &rect, const std::shared_ptr &pTrack ) + : SliderHandle{ sliderFn, rect, pTrack } +{} GainSliderHandle::~GainSliderHandle() { } -GainSliderHandle &GainSliderHandle::Instance() -{ - static GainSliderHandle instance; - return instance; -} - std::shared_ptr GainSliderHandle::GetWaveTrack() { return std::static_pointer_cast(mpTrack.lock()); @@ -70,7 +64,8 @@ UIHandle::Result GainSliderHandle::CommitChanges } HitTestResult GainSliderHandle::HitTest -(const wxMouseState &state, const wxRect &rect, +(std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const AudacityProject *, const std::shared_ptr &pTrack) { if (!state.ButtonIsDown(wxMOUSE_BTN_LEFT)) @@ -83,19 +78,19 @@ HitTestResult GainSliderHandle::HitTest if (sliderRect.Contains(state.m_x, state.m_y)) { wxRect sliderRect; TrackInfo::GetGainRect(rect.GetTopLeft(), sliderRect); - - Instance().mSliderFn = + auto sliderFn = []( AudacityProject *pProject, const wxRect &sliderRect, Track *pTrack ) { return TrackInfo::GainSlider (sliderRect, static_cast( pTrack ), true, const_cast(pProject->GetTrackPanel())); }; - Instance().mRect = sliderRect; - Instance().mpTrack = pTrack; + auto result = + std::make_shared( sliderFn, sliderRect, pTrack ); + result = AssignUIHandlePtr(holder, result); return { HitPreview(), - &Instance() + result }; } else @@ -104,21 +99,15 @@ HitTestResult GainSliderHandle::HitTest //////////////////////////////////////////////////////////////////////////////// -PanSliderHandle::PanSliderHandle() - : SliderHandle() -{ -} +PanSliderHandle::PanSliderHandle +( SliderFn sliderFn, const wxRect &rect, const std::shared_ptr &pTrack ) + : SliderHandle{ sliderFn, rect, pTrack } +{} PanSliderHandle::~PanSliderHandle() { } -PanSliderHandle &PanSliderHandle::Instance() -{ - static PanSliderHandle instance; - return instance; -} - std::shared_ptr PanSliderHandle::GetWaveTrack() { return std::static_pointer_cast(mpTrack.lock()); @@ -167,7 +156,8 @@ UIHandle::Result PanSliderHandle::CommitChanges } HitTestResult PanSliderHandle::HitTest -(const wxMouseState &state, const wxRect &rect, +(std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr &pTrack) { if (!state.ButtonIsDown(wxMOUSE_BTN_LEFT)) @@ -178,18 +168,19 @@ HitTestResult PanSliderHandle::HitTest if ( TrackInfo::HideTopItem( rect, sliderRect, kTrackInfoSliderAllowance ) ) return {}; if (sliderRect.Contains(state.m_x, state.m_y)) { - Instance().mSliderFn = + auto sliderFn = []( AudacityProject *pProject, const wxRect &sliderRect, Track *pTrack ) { return TrackInfo::PanSlider (sliderRect, static_cast( pTrack ), true, const_cast(pProject->GetTrackPanel())); }; - Instance().mRect = sliderRect; - Instance().mpTrack = pTrack; + auto result = std::make_shared( + sliderFn, sliderRect, pTrack ); + result = AssignUIHandlePtr(holder, result); return { HitPreview(), - &Instance() + result }; } else diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackSliderHandles.h b/src/tracks/playabletrack/wavetrack/ui/WaveTrackSliderHandles.h index 618c625c5..fb3e1f512 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackSliderHandles.h +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackSliderHandles.h @@ -21,14 +21,18 @@ struct HitTestResult; class GainSliderHandle final : public SliderHandle { GainSliderHandle(const GainSliderHandle&) = delete; - GainSliderHandle &operator=(const GainSliderHandle&) = delete; - - GainSliderHandle(); - virtual ~GainSliderHandle(); - static GainSliderHandle& Instance(); std::shared_ptr GetWaveTrack(); +public: + explicit GainSliderHandle + ( SliderFn sliderFn, const wxRect &rect, + const std::shared_ptr &pTrack ); + + GainSliderHandle &operator=(const GainSliderHandle&) = default; + + virtual ~GainSliderHandle(); + protected: float GetValue() override; Result SetValue @@ -40,7 +44,8 @@ protected: public: static HitTestResult HitTest - (const wxMouseState &state, const wxRect &rect, + (std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr &pTrack); }; @@ -49,14 +54,18 @@ public: class PanSliderHandle final : public SliderHandle { PanSliderHandle(const PanSliderHandle&) = delete; - PanSliderHandle &operator=(const PanSliderHandle&) = delete; - - PanSliderHandle(); - virtual ~PanSliderHandle(); - static PanSliderHandle& Instance(); std::shared_ptr GetWaveTrack(); +public: + explicit PanSliderHandle + ( SliderFn sliderFn, const wxRect &rect, + const std::shared_ptr &pTrack ); + + PanSliderHandle &operator=(const PanSliderHandle&) = default; + + virtual ~PanSliderHandle(); + protected: float GetValue() override; Result SetValue(AudacityProject *pProject, float newValue) override; @@ -67,7 +76,8 @@ protected: public: static HitTestResult HitTest - (const wxMouseState &state, const wxRect &rect, + (std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr &pTrack); }; diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackUI.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackUI.cpp index 3eb31abce..2501b7d7c 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackUI.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackUI.cpp @@ -39,44 +39,51 @@ HitTestResult WaveTrack::DetailedHitTest // Ctrl modifier key in multi-tool overrides everything else // (But this does not do the time shift constrained to the vertical only, // which is what happens when you hold Ctrl in the Time Shift tool mode) - return TimeShiftHandle::HitAnywhere(pProject); + return TimeShiftHandle::HitAnywhere( + mTimeShiftHandle, pProject, Pointer(this), false); // Some special targets are not drawn in spectrogram, // so don't hit them in such views. else if (isWaveform) { HitTestResult result; - if (NULL != - (result = CutlineHandle::HitTest - (st.state, st.rect, pProject, Pointer(this))) - .preview.cursor) - // This overriding test applies in all tools - return result; + if (NULL != (result = CutlineHandle::HitTest( + mCutlineHandle, st.state, st.rect, + pProject, Pointer(this))).preview.cursor) + // This overriding test applies in all tools + return result; else if (bMultiTool) { // Conditional hit tests // If Tools toolbar were eliminated, we would keep these // The priority of these, in case more than one might apply at one // point, seems arbitrary - if (NULL != (result = EnvelopeHandle::WaveTrackHitTest - (st.state, st.rect, pProject, Pointer(this))) - .preview.cursor) + if (NULL != (result = EnvelopeHandle::WaveTrackHitTest( + mEnvelopeHandle, st.state, st.rect, + pProject, Pointer(this))) + .preview.cursor) ; - else if (NULL != (result = TimeShiftHandle::HitTest - (st.state, st.rect, pProject)).preview.cursor) + else if (NULL != (result = TimeShiftHandle::HitTest( + mTimeShiftHandle, st.state, st.rect, + pProject, Pointer(this))).preview.cursor) // This is the hit test on the "grips" drawn left and // right in Multi only ; - else if (NULL != (result = SampleHandle::HitTest - (st.state, st.rect, pProject, Pointer(this))).preview.cursor) + else if (NULL != (result = SampleHandle::HitTest( + mSampleHandle, st.state, st.rect, + pProject, Pointer(this))).preview.cursor) ; return result; } else switch ( currentTool ) { // Unconditional hits appropriate to the tool // If tools toolbar were eliminated, we would eliminate these - case envelopeTool: - return EnvelopeHandle::HitAnywhere(pProject); + case envelopeTool: { + auto envelope = GetEnvelopeAtX( st.state.m_x ); + return EnvelopeHandle::HitAnywhere( + mEnvelopeHandle, pProject, envelope); + } case drawTool: - return SampleHandle::HitAnywhere(st.state, pProject); + return SampleHandle::HitAnywhere( + mSampleHandle, st.state, pProject, Pointer(this)); default: break; } diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackVRulerControls.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackVRulerControls.cpp index 555b3277e..1b6f89ba0 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackVRulerControls.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackVRulerControls.cpp @@ -33,7 +33,14 @@ HitTestResult WaveTrackVRulerControls::HitTest (const TrackPanelMouseState &st, const AudacityProject *) { - return WaveTrackVZoomHandle::HitTest(st.state); + auto pTrack = Track::Pointer( FindTrack().get() ); + if (pTrack) { + auto result = std::make_shared( + pTrack, st.rect, st.state.m_y ); + result = AssignUIHandlePtr(mVZoomHandle, result); + return WaveTrackVZoomHandle::HitTest(st.state, result); + } + return {}; } unsigned WaveTrackVRulerControls::HandleWheelRotation diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackVRulerControls.h b/src/tracks/playabletrack/wavetrack/ui/WaveTrackVRulerControls.h index 5691c6c02..011c2daaa 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackVRulerControls.h +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackVRulerControls.h @@ -13,6 +13,8 @@ Paul Licameli split from TrackPanel.cpp #include "../../../ui/TrackVRulerControls.h" +class WaveTrackVZoomHandle; + class WaveTrackVRulerControls final : public TrackVRulerControls { WaveTrackVRulerControls(const WaveTrackVRulerControls&) = delete; @@ -31,6 +33,9 @@ public: unsigned HandleWheelRotation (const TrackPanelMouseEvent &event, AudacityProject *pProject) override; + +private: + std::weak_ptr mVZoomHandle; }; #endif diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackVZoomHandle.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackVZoomHandle.cpp index 1362b41f2..be2b08723 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackVZoomHandle.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackVZoomHandle.cpp @@ -43,6 +43,12 @@ bool IsDragZooming(int zoomStart, int zoomEnd) } +WaveTrackVZoomHandle::WaveTrackVZoomHandle +(const std::shared_ptr &pTrack, const wxRect &rect, int y) + : mZoomStart(y), mZoomEnd(y), mRect(rect) + , mpTrack{ pTrack } +{} + void WaveTrackVZoomHandle::DoZoom (AudacityProject *pProject, WaveTrack *pTrack, bool shiftDown, bool rightUp, @@ -480,16 +486,6 @@ void SpectrumVRulerMenuTable::OnSpectrumScaleType(wxCommandEvent &evt) /////////////////////////////////////////////////////////////////////////////// -WaveTrackVZoomHandle::WaveTrackVZoomHandle() -{ -} - -WaveTrackVZoomHandle &WaveTrackVZoomHandle::Instance() -{ - static WaveTrackVZoomHandle instance; - return instance; -} - HitTestPreview WaveTrackVZoomHandle::HitPreview(const wxMouseState &state) { static auto zoomInCursor = @@ -502,9 +498,10 @@ HitTestPreview WaveTrackVZoomHandle::HitPreview(const wxMouseState &state) }; } -HitTestResult WaveTrackVZoomHandle::HitTest(const wxMouseState &state) +HitTestResult WaveTrackVZoomHandle::HitTest +(const wxMouseState &state, UIHandlePtr result) { - return { HitPreview(state), &Instance() }; + return { HitPreview(state), result }; } WaveTrackVZoomHandle::~WaveTrackVZoomHandle() @@ -512,16 +509,8 @@ WaveTrackVZoomHandle::~WaveTrackVZoomHandle() } UIHandle::Result WaveTrackVZoomHandle::Click -(const TrackPanelMouseEvent &evt, AudacityProject *) +(const TrackPanelMouseEvent &, AudacityProject *) { - mpTrack = std::static_pointer_cast( - static_cast(evt.pCell.get())->FindTrack() ); - mRect = evt.rect; - - const wxMouseEvent &event = evt.event; - mZoomStart = event.m_y; - mZoomEnd = event.m_y; - return RefreshCode::RefreshNone; } diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackVZoomHandle.h b/src/tracks/playabletrack/wavetrack/ui/WaveTrackVZoomHandle.h index 9952af9dc..04c47ebd4 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackVZoomHandle.h +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackVZoomHandle.h @@ -21,14 +21,16 @@ struct HitTestResult; class WaveTrackVZoomHandle : public UIHandle { - WaveTrackVZoomHandle(); WaveTrackVZoomHandle(const WaveTrackVZoomHandle&); - WaveTrackVZoomHandle &operator=(const WaveTrackVZoomHandle&); - static WaveTrackVZoomHandle& Instance(); static HitTestPreview HitPreview(const wxMouseState &state); public: - static HitTestResult HitTest(const wxMouseState &state); + explicit WaveTrackVZoomHandle + (const std::shared_ptr &pTrack, const wxRect &rect, int y); + + WaveTrackVZoomHandle &operator=(const WaveTrackVZoomHandle&) = default; + + static HitTestResult HitTest(const wxMouseState &state, UIHandlePtr result); static void DoZoom (AudacityProject *pProject, @@ -38,6 +40,8 @@ public: virtual ~WaveTrackVZoomHandle(); + std::shared_ptr GetTrack() const { return mpTrack.lock(); } + Result Click (const TrackPanelMouseEvent &event, AudacityProject *pProject) override; diff --git a/src/tracks/timetrack/ui/TimeTrackUI.cpp b/src/tracks/timetrack/ui/TimeTrackUI.cpp index 88aa72bbf..1d724c2d7 100644 --- a/src/tracks/timetrack/ui/TimeTrackUI.cpp +++ b/src/tracks/timetrack/ui/TimeTrackUI.cpp @@ -23,7 +23,7 @@ HitTestResult TimeTrack::DetailedHitTest const AudacityProject *pProject, int, bool) { return EnvelopeHandle::TimeTrackHitTest - ( st.state, st.rect, pProject, Pointer(this) ); + ( mEnvelopeHandle, st.state, st.rect, pProject, Pointer(this) ); } std::shared_ptr TimeTrack::GetControls() diff --git a/src/tracks/ui/BackgroundCell.cpp b/src/tracks/ui/BackgroundCell.cpp index 5c908fbfc..c5acb34d7 100644 --- a/src/tracks/ui/BackgroundCell.cpp +++ b/src/tracks/ui/BackgroundCell.cpp @@ -20,76 +20,68 @@ Paul Licameli split from TrackPanel.cpp #include #include -namespace +// Define this, just so the click to deselect can dispatch here +// This handle class, unlike most, doesn't associate with any particular cell. +class BackgroundHandle : public UIHandle { - // Define this, just so the click to deselect can dispatch here - class BackgroundHandle : public UIHandle + BackgroundHandle(const BackgroundHandle&) = delete; + BackgroundHandle &operator=(const BackgroundHandle&) = delete; + +public: + BackgroundHandle() {} + + static HitTestPreview HitPreview() { - BackgroundHandle() {} - BackgroundHandle(const BackgroundHandle&) = delete; - BackgroundHandle &operator=(const BackgroundHandle&) = delete; + static wxCursor arrowCursor{ wxCURSOR_ARROW }; + return { {}, &arrowCursor }; + } - public: + static HitTestResult HitAnywhere(UIHandlePtr result) + { + return { + HitPreview(), + result + }; + } - static BackgroundHandle& Instance() - { - static BackgroundHandle instance; - return instance; + virtual ~BackgroundHandle() + {} + + Result Click + (const TrackPanelMouseEvent &evt, AudacityProject *pProject) override + { + using namespace RefreshCode; + const wxMouseEvent &event = evt.event; + // Do not start a drag + Result result = Cancelled; + + // AS: If the user clicked outside all tracks, make nothing + // selected. + if ((event.ButtonDown() || event.ButtonDClick())) { + pProject->GetSelectionState().SelectNone + ( *pProject->GetTracks(), pProject->GetMixerBoard() ); + result |= RefreshAll; } - static HitTestPreview HitPreview() - { - static wxCursor arrowCursor{ wxCURSOR_ARROW }; - return { {}, &arrowCursor }; - } + return result; + } - static HitTestResult HitAnywhere() - { - return { - HitPreview(), - &BackgroundHandle::Instance() - }; - } + Result Drag + (const TrackPanelMouseEvent &, AudacityProject *) override + { return RefreshCode::RefreshNone; } - virtual ~BackgroundHandle() - {} + HitTestPreview Preview + (const TrackPanelMouseState &, const AudacityProject *) override + { return HitPreview(); } - Result Click - (const TrackPanelMouseEvent &evt, AudacityProject *pProject) override - { - using namespace RefreshCode; - const wxMouseEvent &event = evt.event; - // Do not start a drag - Result result = Cancelled; + Result Release + (const TrackPanelMouseEvent &, AudacityProject *, + wxWindow *) override + { return RefreshCode::RefreshNone; } - // AS: If the user clicked outside all tracks, make nothing - // selected. - if ((event.ButtonDown() || event.ButtonDClick())) { - pProject->GetSelectionState().SelectNone - ( *pProject->GetTracks(), pProject->GetMixerBoard() ); - result |= RefreshAll; - } - - return result; - } - - Result Drag - (const TrackPanelMouseEvent &, AudacityProject *) override - { return RefreshCode::RefreshNone; } - - HitTestPreview Preview - (const TrackPanelMouseState &, const AudacityProject *) override - { return HitPreview(); } - - Result Release - (const TrackPanelMouseEvent &, AudacityProject *, - wxWindow *) override - { return RefreshCode::RefreshNone; } - - Result Cancel(AudacityProject *) override - { return RefreshCode::RefreshNone; } - }; -} + Result Cancel(AudacityProject *) override + { return RefreshCode::RefreshNone; } +}; BackgroundCell::~BackgroundCell() { @@ -99,7 +91,10 @@ HitTestResult BackgroundCell::HitTest (const TrackPanelMouseState &, const AudacityProject *) { - return BackgroundHandle::HitAnywhere(); + auto result = mHandle.lock(); + if (!result) + result = std::make_shared(); + return BackgroundHandle::HitAnywhere(result); } std::shared_ptr BackgroundCell::FindTrack() diff --git a/src/tracks/ui/BackgroundCell.h b/src/tracks/ui/BackgroundCell.h index cab60f9a7..a821026fb 100644 --- a/src/tracks/ui/BackgroundCell.h +++ b/src/tracks/ui/BackgroundCell.h @@ -12,9 +12,13 @@ Paul Licameli split from TrackPanel.cpp #define __AUDACITY_BACKGROUND_CELL__ #include "CommonTrackPanelCell.h" +#include "../../MemoryX.h" class AudacityProject; +class BackgroundHandle; +class ZoomHandle; + class BackgroundCell final : public CommonTrackPanelCell { public: @@ -33,6 +37,12 @@ protected: private: AudacityProject *mpProject; + + std::weak_ptr mHandle; + +public: + // For want of a better place... + std::weak_ptr mZoomHandle; }; #endif diff --git a/src/tracks/ui/ButtonHandle.cpp b/src/tracks/ui/ButtonHandle.cpp index 045c5a443..482982f22 100644 --- a/src/tracks/ui/ButtonHandle.cpp +++ b/src/tracks/ui/ButtonHandle.cpp @@ -20,10 +20,12 @@ Paul Licameli #include "../../TrackPanelMouseEvent.h" #include "../ui/TrackControls.h" -ButtonHandle::ButtonHandle(int dragCode) - : mDragCode(dragCode) -{ -} +ButtonHandle::ButtonHandle +( const std::shared_ptr &pTrack, const wxRect &rect, int dragCode ) + : mpTrack{ pTrack } + , mRect{ rect } + , mDragCode{ dragCode } +{} ButtonHandle::~ButtonHandle() { @@ -36,10 +38,10 @@ HitTestPreview ButtonHandle::HitPreview() } UIHandle::Result ButtonHandle::Click -(const TrackPanelMouseEvent &evt, AudacityProject *) +(const TrackPanelMouseEvent &evt, AudacityProject *pProject) { using namespace RefreshCode; - auto pTrack = static_cast(evt.pCell.get())->FindTrack(); + auto pTrack = pProject->GetTracks()->Lock(mpTrack); if ( !pTrack ) return Cancelled; @@ -49,7 +51,6 @@ UIHandle::Result ButtonHandle::Click // Come here for left click or double click if (mRect.Contains(event.m_x, event.m_y)) { - mpTrack = pTrack; TrackControls::gCaptureState = mDragCode; // Toggle visible button state return RefreshCell; diff --git a/src/tracks/ui/ButtonHandle.h b/src/tracks/ui/ButtonHandle.h index d1279af36..4c871ede7 100644 --- a/src/tracks/ui/ButtonHandle.h +++ b/src/tracks/ui/ButtonHandle.h @@ -22,10 +22,13 @@ class Track; class ButtonHandle /* not final */ : public UIHandle { ButtonHandle(const ButtonHandle&) = delete; - ButtonHandle &operator=(const ButtonHandle&) = delete; protected: - explicit ButtonHandle(int dragCode); + explicit ButtonHandle + ( const std::shared_ptr &pTrack, const wxRect &rect, int dragCode ); + + ButtonHandle &operator=(const ButtonHandle&) = default; + virtual ~ButtonHandle(); // This new abstract virtual simplifies the duties of further subclasses. @@ -54,9 +57,9 @@ protected: Result Cancel(AudacityProject *pProject) override; - wxRect mRect {}; std::weak_ptr mpTrack; - const int mDragCode; + wxRect mRect; + int mDragCode; }; #endif diff --git a/src/tracks/ui/EnvelopeHandle.cpp b/src/tracks/ui/EnvelopeHandle.cpp index 7cfed99b8..bd46eabf8 100644 --- a/src/tracks/ui/EnvelopeHandle.cpp +++ b/src/tracks/ui/EnvelopeHandle.cpp @@ -26,15 +26,12 @@ Paul Licameli split from TrackPanel.cpp #include "../../WaveTrack.h" #include "../../../images/Cursors.h" -EnvelopeHandle::EnvelopeHandle() -{ -} +EnvelopeHandle::EnvelopeHandle( Envelope *pEnvelope ) + : mEnvelope{ pEnvelope } +{} -EnvelopeHandle &EnvelopeHandle::Instance() -{ - static EnvelopeHandle instance; - return instance; -} +EnvelopeHandle::~EnvelopeHandle() +{} HitTestPreview EnvelopeHandle::HitPreview(const AudacityProject *pProject, bool unsafe) { @@ -51,10 +48,13 @@ HitTestPreview EnvelopeHandle::HitPreview(const AudacityProject *pProject, bool }; } -HitTestResult EnvelopeHandle::HitAnywhere(const AudacityProject *pProject) +HitTestResult EnvelopeHandle::HitAnywhere +(std::weak_ptr &holder, + const AudacityProject *pProject, Envelope *envelope) { const bool unsafe = pProject->IsAudioActive(); - return { HitPreview(pProject, unsafe), &Instance() }; + UIHandlePtr result = std::make_shared( envelope ); + return { HitPreview(pProject, unsafe), result }; } namespace { @@ -75,7 +75,8 @@ namespace { } HitTestResult EnvelopeHandle::TimeTrackHitTest -(const wxMouseState &state, const wxRect &rect, +(std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr &tt) { auto envelope = tt->GetEnvelope(); @@ -86,16 +87,17 @@ HitTestResult EnvelopeHandle::TimeTrackHitTest float zoomMin, zoomMax; GetTimeTrackData( *pProject, *tt, dBRange, dB, zoomMin, zoomMax); return EnvelopeHandle::HitEnvelope - (state, rect, pProject, envelope, zoomMin, zoomMax, dB, dBRange); + (holder, state, rect, pProject, envelope, zoomMin, zoomMax, dB, dBRange); } HitTestResult EnvelopeHandle::WaveTrackHitTest -(const wxMouseState &state, const wxRect &rect, +(std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr &wt) { /// method that tells us if the mouse event landed on an /// envelope boundary. - const Envelope *const envelope = wt->GetEnvelopeAtX(state.GetX()); + Envelope *const envelope = wt->GetEnvelopeAtX(state.GetX()); if (!envelope) return {}; @@ -115,12 +117,13 @@ HitTestResult EnvelopeHandle::WaveTrackHitTest const float dBRange = wt->GetWaveformSettings().dBRange; return EnvelopeHandle::HitEnvelope - (state, rect, pProject, envelope, zoomMin, zoomMax, dB, dBRange); + (holder, state, rect, pProject, envelope, zoomMin, zoomMax, dB, dBRange); } HitTestResult EnvelopeHandle::HitEnvelope -(const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, - const Envelope *envelope, float zoomMin, float zoomMax, +(std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, + Envelope *envelope, float zoomMin, float zoomMax, bool dB, float dBRange) { const ViewInfo &viewInfo = pProject->GetViewInfo(); @@ -167,11 +170,7 @@ HitTestResult EnvelopeHandle::HitEnvelope if (distance >= yTolerance) return {}; - return HitAnywhere(pProject); -} - -EnvelopeHandle::~EnvelopeHandle() -{ + return HitAnywhere(holder, pProject, envelope); } UIHandle::Result EnvelopeHandle::Click @@ -191,23 +190,21 @@ UIHandle::Result EnvelopeHandle::Click if (wt->GetDisplay() != WaveTrack::Waveform) return Cancelled; - auto clickedEnvelope = - wt->GetEnvelopeAtX(event.GetX()); - if (!clickedEnvelope) + if (!mEnvelope) return Cancelled; mLog = !wt->GetWaveformSettings().isLinear(); wt->GetDisplayBounds(&mLower, &mUpper); mdBRange = wt->GetWaveformSettings().dBRange; mEnvelopeEditor = - std::make_unique< EnvelopeEditor >( *clickedEnvelope, true ); + std::make_unique< EnvelopeEditor >( *mEnvelope, true ); mEnvelopeEditorRight.reset(); // Assume linked track is wave or null auto partner = static_cast(wt->GetLink()); if (partner) { - clickedEnvelope = partner->GetEnvelopeAtX(event.GetX()); + auto clickedEnvelope = partner->GetEnvelopeAtX(event.GetX()); if (clickedEnvelope) mEnvelopeEditorRight = std::make_unique< EnvelopeEditor >( *clickedEnvelope, true ); @@ -216,12 +213,11 @@ UIHandle::Result EnvelopeHandle::Click else if (pTrack->GetKind() == Track::Time) { TimeTrack *const tt = static_cast(pTrack); - auto clickedEnvelope = tt->GetEnvelope(); - if (!clickedEnvelope) + if (!mEnvelope) return Cancelled; GetTimeTrackData( *pProject, *tt, mdBRange, mLog, mLower, mUpper); mEnvelopeEditor = - std::make_unique< EnvelopeEditor >( *clickedEnvelope, false ); + std::make_unique< EnvelopeEditor >( *mEnvelope, false ); mEnvelopeEditorRight.reset(); } else diff --git a/src/tracks/ui/EnvelopeHandle.h b/src/tracks/ui/EnvelopeHandle.h index c79179b9a..4ac3de630 100644 --- a/src/tracks/ui/EnvelopeHandle.h +++ b/src/tracks/ui/EnvelopeHandle.h @@ -27,29 +27,36 @@ 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); static HitTestResult HitEnvelope - (const wxMouseState &state, const wxRect &rect, + (std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, - const Envelope *envelope, float zoomMin, float zoomMax, + Envelope *envelope, float zoomMin, float zoomMax, bool dB, float dBRange); public: - static HitTestResult HitAnywhere(const AudacityProject *pProject); - static HitTestResult TimeTrackHitTest - (const wxMouseState &state, const wxRect &rect, - const AudacityProject *pProject, const std::shared_ptr &tt); - static HitTestResult WaveTrackHitTest - (const wxMouseState &state, const wxRect &rect, - const AudacityProject *pProject, const std::shared_ptr &wt); + explicit EnvelopeHandle( Envelope *pEnvelope ); virtual ~EnvelopeHandle(); + static HitTestResult HitAnywhere + (std::weak_ptr &holder, + const AudacityProject *pProject, Envelope *envelope); + static HitTestResult TimeTrackHitTest + (std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, + const AudacityProject *pProject, const std::shared_ptr &tt); + static HitTestResult WaveTrackHitTest + (std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, + const AudacityProject *pProject, const std::shared_ptr &wt); + + Envelope *GetEnvelope() const { return mEnvelope; } + Result Click (const TrackPanelMouseEvent &event, AudacityProject *pProject) override; @@ -77,6 +84,7 @@ private: float mLower{}, mUpper{}; double mdBRange{}; + Envelope *mEnvelope{}; std::unique_ptr mEnvelopeEditor; std::unique_ptr mEnvelopeEditorRight; }; diff --git a/src/tracks/ui/SelectHandle.cpp b/src/tracks/ui/SelectHandle.cpp index 4c1443dca..e53dd4774 100644 --- a/src/tracks/ui/SelectHandle.cpp +++ b/src/tracks/ui/SelectHandle.cpp @@ -8,6 +8,7 @@ Paul Licameli split from TrackPanel.cpp **********************************************************************/ +#include "../../Audacity.h" #include "SelectHandle.h" #include "Scrubbing.h" @@ -54,16 +55,6 @@ bool SelectHandle::IsClicked() const return mSelectionStateChanger.get(); } -SelectHandle::SelectHandle() -{ -} - -SelectHandle &SelectHandle::Instance() -{ - static SelectHandle instance; - return instance; -} - namespace { // If we're in OnDemand mode, we may change the tip. @@ -428,9 +419,31 @@ namespace } HitTestResult SelectHandle::HitTest -(const TrackPanelMouseState &st, const AudacityProject *pProject, +(std::weak_ptr &holder, + const TrackPanelMouseState &st, const AudacityProject *pProject, const std::shared_ptr &pTrack) { + // This handle is a little special, not following the pattern of calling + // AssignUIHandlePtr(); there may be some state to preserve during movement + // before the click. + auto old = holder.lock(); + std::unique_ptr oldSnapManager; + wxInt64 oldSnapLeft = -1, oldSnapRight = -1; + if (old) { + // It should not have started listening to timer events + wxASSERT( !old->mConnectedProject ); + oldSnapManager = std::move(old->mSnapManager); + oldSnapLeft = old->mSnapLeft; + oldSnapRight = old->mSnapRight; + } + + auto result = std::make_shared( pTrack ); + + // Copy the pre-dragging state + result->mSnapManager = std::move( oldSnapManager ); + result->mSnapLeft = oldSnapLeft; + result->mSnapRight = oldSnapRight; + const wxMouseState &state = st.state; const wxRect &rect = st.rect; @@ -465,7 +478,7 @@ HitTestResult SelectHandle::HitTest if (!pTrack->GetSelected() || !viewInfo.bAdjustSelectionEdges) { MaySetOnDemandTip(pTrack.get(), tip); - return { { tip, pCursor }, &Instance() }; + return { { tip, pCursor }, result }; } { @@ -510,9 +523,13 @@ HitTestResult SelectHandle::HitTest tip = ttb->GetMessageForTool(selectTool); } - return HitTestResult{ { tip, pCursor }, &Instance() }; + return HitTestResult{ { tip, pCursor }, result }; } +SelectHandle::SelectHandle( const std::shared_ptr &pTrack ) + : mpTrack{ pTrack } +{} + SelectHandle::~SelectHandle() { } @@ -545,7 +562,8 @@ UIHandle::Result SelectHandle::Click using namespace RefreshCode; wxMouseEvent &event = evt.event; - const auto pTrack = static_cast(evt.pCell.get()); + const auto sTrack = pProject->GetTracks()->Lock(mpTrack); + const auto pTrack = sTrack.get(); ViewInfo &viewInfo = pProject->GetViewInfo(); mMostRecentX = event.m_x; @@ -598,7 +616,6 @@ UIHandle::Result SelectHandle::Click else if (!event.LeftDown()) return Cancelled; - mpTrack = Track::Pointer( pTrack ); mRect = evt.rect; mInitialSelection = viewInfo.selectedRegion; @@ -608,11 +625,12 @@ UIHandle::Result SelectHandle::Click mSelectionBoundary = 0; - // We create a NEW snap manager in case any snap-points have changed - mSnapManager = std::make_unique(trackList, &viewInfo); - - mSnapLeft = -1; - mSnapRight = -1; + if (!mSnapManager) { + // We create a NEW snap manager in case any snap-points have changed + mSnapManager = std::make_unique(trackList, &viewInfo); + mSnapLeft = -1; + mSnapRight = -1; + } bool bShiftDown = event.ShiftDown(); bool bCtrlDown = event.ControlDown(); diff --git a/src/tracks/ui/SelectHandle.h b/src/tracks/ui/SelectHandle.h index c7b4f20f0..8bdeac78a 100644 --- a/src/tracks/ui/SelectHandle.h +++ b/src/tracks/ui/SelectHandle.h @@ -30,17 +30,17 @@ class WaveTrack; class SelectHandle : public wxEvtHandler, public UIHandle { - SelectHandle(); SelectHandle(const SelectHandle&); SelectHandle &operator=(const SelectHandle&); public: - static SelectHandle& Instance(); + explicit SelectHandle( const std::shared_ptr &pTrack ); // This always hits, but details of the hit vary with mouse position and // key state. static HitTestResult HitTest - (const TrackPanelMouseState &state, const AudacityProject *pProject, + (std::weak_ptr &holder, + const TrackPanelMouseState &state, const AudacityProject *pProject, const std::shared_ptr &pTrack); virtual ~SelectHandle(); diff --git a/src/tracks/ui/SliderHandle.cpp b/src/tracks/ui/SliderHandle.cpp index 5b8b48cfa..7001ed09d 100644 --- a/src/tracks/ui/SliderHandle.cpp +++ b/src/tracks/ui/SliderHandle.cpp @@ -16,9 +16,12 @@ Paul Licameli #include "../../RefreshCode.h" #include "../../TrackPanelMouseEvent.h" -SliderHandle::SliderHandle() -{ -} +SliderHandle::SliderHandle +( SliderFn sliderFn, const wxRect &rect, const std::shared_ptr &pTrack ) + : mSliderFn{ sliderFn } + , mRect{ rect } + , mpTrack{ pTrack } +{} SliderHandle::~SliderHandle() { diff --git a/src/tracks/ui/SliderHandle.h b/src/tracks/ui/SliderHandle.h index 9fa78cb0c..188154dda 100644 --- a/src/tracks/ui/SliderHandle.h +++ b/src/tracks/ui/SliderHandle.h @@ -22,10 +22,17 @@ class Track; class SliderHandle /* not final */ : public UIHandle { SliderHandle(const SliderHandle&) = delete; - SliderHandle &operator=(const SliderHandle&) = delete; + +public: + using SliderFn = LWSlider *(*)( AudacityProject*, const wxRect&, Track* ); + + explicit SliderHandle + ( SliderFn sliderFn, const wxRect &rect, + const std::shared_ptr &pTrack ); + + SliderHandle &operator=(const SliderHandle&) = default; protected: - SliderHandle(); virtual ~SliderHandle(); // These new abstract virtuals simplify the duties of further subclasses. @@ -59,7 +66,6 @@ protected: // Derived class is expected to set these two before Click(): std::weak_ptr mpTrack; wxRect mRect{}; - using SliderFn = LWSlider *(*)( AudacityProject*, const wxRect&, Track* ); SliderFn mSliderFn; LWSlider *GetSlider( AudacityProject *pProject ); diff --git a/src/tracks/ui/TimeShiftHandle.cpp b/src/tracks/ui/TimeShiftHandle.cpp index 2ec528eb9..bf246a92e 100644 --- a/src/tracks/ui/TimeShiftHandle.cpp +++ b/src/tracks/ui/TimeShiftHandle.cpp @@ -23,15 +23,11 @@ Paul Licameli split from TrackPanel.cpp #include "../../WaveTrack.h" #include "../../../images/Cursors.h" -TimeShiftHandle::TimeShiftHandle() -{ -} - -TimeShiftHandle &TimeShiftHandle::Instance() -{ - static TimeShiftHandle instance; - return instance; -} +TimeShiftHandle::TimeShiftHandle +( const std::shared_ptr &pTrack, bool gripHit ) + : mCapturedTrack{ pTrack } + , mGripHit{ gripHit } +{} HitTestPreview TimeShiftHandle::HitPreview (const AudacityProject *pProject, bool unsafe) @@ -49,16 +45,24 @@ HitTestPreview TimeShiftHandle::HitPreview }; } -HitTestResult TimeShiftHandle::HitAnywhere(const AudacityProject *pProject) +HitTestResult TimeShiftHandle::HitAnywhere +(std::weak_ptr &holder, + const AudacityProject *pProject, + const std::shared_ptr &pTrack, bool gripHit) { // After all that, it still may be unsafe to drag. // Even if so, make an informative cursor change from default to "banned." const bool unsafe = pProject->IsAudioActive(); - return { HitPreview(pProject, unsafe), &Instance() }; + auto result = std::make_shared( pTrack, gripHit ); + result = AssignUIHandlePtr(holder, result); + return { HitPreview(pProject, unsafe), result }; } HitTestResult TimeShiftHandle::HitTest - (const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject) +(std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, + const AudacityProject *pProject, + const std::shared_ptr &pTrack) { /// method that tells us if the mouse event landed on a /// time-slider that allows us to time shift the sequence. @@ -77,7 +81,7 @@ HitTestResult TimeShiftHandle::HitTest state.m_x + hotspotOffset >= rect.x + rect.width - adjustedDragHandleWidth)) return {}; - return HitAnywhere(pProject); + return HitAnywhere( holder, pProject, pTrack, true ); } TimeShiftHandle::~TimeShiftHandle() @@ -465,7 +469,6 @@ UIHandle::Result TimeShiftHandle::Click } mSlideUpDownOnly = event.CmdDown() && !multiToolModeActive; - mCapturedTrack = pTrack; mRect = rect; mMouseClickX = event.m_x; const double selStart = viewInfo.PositionToTime(event.m_x, mRect.x); @@ -506,6 +509,7 @@ UIHandle::Result TimeShiftHandle::Drag track = mCapturedTrack.get(); } + // May need a shared_ptr to reassign mCapturedTrack below auto pTrack = Track::Pointer( track ); if (!pTrack) return RefreshCode::RefreshNone; diff --git a/src/tracks/ui/TimeShiftHandle.h b/src/tracks/ui/TimeShiftHandle.h index 3aa0918e4..f925414a4 100644 --- a/src/tracks/ui/TimeShiftHandle.h +++ b/src/tracks/ui/TimeShiftHandle.h @@ -21,6 +21,8 @@ Paul Licameli struct HitTestResult; class WaveClip; +using UIHandlePtr = std::shared_ptr; + struct ClipMoveState { // non-NULL only if click was in a WaveTrack and without Shift key: WaveClip *capturedClip {}; @@ -44,14 +46,19 @@ struct ClipMoveState { class TimeShiftHandle final : public UIHandle { - TimeShiftHandle(); TimeShiftHandle(const TimeShiftHandle&) = delete; - TimeShiftHandle &operator=(const TimeShiftHandle&) = delete; - static TimeShiftHandle& Instance(); static HitTestPreview HitPreview (const AudacityProject *pProject, bool unsafe); public: + explicit TimeShiftHandle + ( const std::shared_ptr &pTrack, bool gripHit ); + + TimeShiftHandle &operator=(TimeShiftHandle&&) = default; + + bool IsGripHit() const { return mGripHit; } + std::shared_ptr GetTrack() const { return mCapturedTrack; } + // A utility function also used by menu commands static void CreateListOfCapturedClips ( ClipMoveState &state, const ViewInfo &viewInfo, Track &capturedTrack, @@ -61,9 +68,15 @@ public: static void DoSlideHorizontal ( ClipMoveState &state, TrackList &trackList, Track &capturedTrack ); - static HitTestResult HitAnywhere(const AudacityProject *pProject); + static HitTestResult HitAnywhere + (std::weak_ptr &holder, + const AudacityProject *pProject, + const std::shared_ptr &pTrack, bool gripHit); static HitTestResult HitTest - (const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject); + (std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, + const AudacityProject *pProject, + const std::shared_ptr &pTrack); virtual ~TimeShiftHandle(); @@ -107,6 +120,7 @@ private: std::unique_ptr mSnapManager{}; ClipMoveState mClipMoveState{}; + bool mGripHit {}; }; #endif diff --git a/src/tracks/ui/TrackButtonHandles.cpp b/src/tracks/ui/TrackButtonHandles.cpp index 6e1331643..2ae4eef65 100644 --- a/src/tracks/ui/TrackButtonHandles.cpp +++ b/src/tracks/ui/TrackButtonHandles.cpp @@ -17,21 +17,15 @@ Paul Licameli split from TrackPanel.cpp #include "../../Track.h" #include "../../TrackPanel.h" -MinimizeButtonHandle::MinimizeButtonHandle() - : ButtonHandle{ TrackPanel::IsMinimizing } -{ -} +MinimizeButtonHandle::MinimizeButtonHandle +( const std::shared_ptr &pTrack, const wxRect &rect ) + : ButtonHandle{ pTrack, rect, TrackPanel::IsMinimizing } +{} MinimizeButtonHandle::~MinimizeButtonHandle() { } -MinimizeButtonHandle &MinimizeButtonHandle::Instance() -{ - static MinimizeButtonHandle instance; - return instance; -} - UIHandle::Result MinimizeButtonHandle::CommitChanges (const wxMouseEvent &, AudacityProject *pProject, wxWindow*) { @@ -55,16 +49,19 @@ UIHandle::Result MinimizeButtonHandle::CommitChanges } HitTestResult MinimizeButtonHandle::HitTest -(const wxMouseState &state, const wxRect &rect) +(std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, TrackPanelCell *pCell) { wxRect buttonRect; TrackInfo::GetMinimizeRect(rect, buttonRect); if (buttonRect.Contains(state.m_x, state.m_y)) { - Instance().mRect = buttonRect; + auto pTrack = static_cast(pCell)->FindTrack(); + auto result = std::make_shared( pTrack, buttonRect ); + result = AssignUIHandlePtr(holder, result); return { HitPreview(), - &Instance() + result }; } else @@ -73,21 +70,15 @@ HitTestResult MinimizeButtonHandle::HitTest //////////////////////////////////////////////////////////////////////////////// -CloseButtonHandle::CloseButtonHandle() - : ButtonHandle{ TrackPanel::IsClosing } -{ -} +CloseButtonHandle::CloseButtonHandle +( const std::shared_ptr &pTrack, const wxRect &rect ) + : ButtonHandle{ pTrack, rect, TrackPanel::IsClosing } +{} CloseButtonHandle::~CloseButtonHandle() { } -CloseButtonHandle &CloseButtonHandle::Instance() -{ - static CloseButtonHandle instance; - return instance; -} - UIHandle::Result CloseButtonHandle::CommitChanges (const wxMouseEvent &, AudacityProject *pProject, wxWindow*) { @@ -112,16 +103,19 @@ UIHandle::Result CloseButtonHandle::CommitChanges } HitTestResult CloseButtonHandle::HitTest -(const wxMouseState &state, const wxRect &rect) +(std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, TrackPanelCell *pCell) { wxRect buttonRect; TrackInfo::GetCloseBoxRect(rect, buttonRect); if (buttonRect.Contains(state.m_x, state.m_y)) { - Instance().mRect = buttonRect; + auto pTrack = static_cast(pCell)->FindTrack(); + auto result = std::make_shared( pTrack, buttonRect ); + result = AssignUIHandlePtr(holder, result); return { HitPreview(), - &Instance() + result }; } else @@ -130,21 +124,17 @@ HitTestResult CloseButtonHandle::HitTest //////////////////////////////////////////////////////////////////////////////// -MenuButtonHandle::MenuButtonHandle() - : ButtonHandle{ TrackPanel::IsPopping } -{ -} +MenuButtonHandle::MenuButtonHandle +( const std::shared_ptr &pCell, + const std::shared_ptr &pTrack, const wxRect &rect ) + : ButtonHandle{ pTrack, rect, TrackPanel::IsPopping } + , mpCell{ pCell } +{} MenuButtonHandle::~MenuButtonHandle() { } -MenuButtonHandle &MenuButtonHandle::Instance() -{ - static MenuButtonHandle instance; - return instance; -} - UIHandle::Result MenuButtonHandle::CommitChanges (const wxMouseEvent &, AudacityProject *, wxWindow *pParent) { @@ -155,18 +145,20 @@ UIHandle::Result MenuButtonHandle::CommitChanges } HitTestResult MenuButtonHandle::HitTest -(const wxMouseState &state, const wxRect &rect, +(std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const std::shared_ptr &pCell) { wxRect buttonRect; TrackInfo::GetTitleBarRect(rect, buttonRect); if (buttonRect.Contains(state.m_x, state.m_y)) { - Instance().mpCell = pCell; - Instance().mRect = buttonRect; + auto pTrack = static_cast(pCell.get())->FindTrack(); + auto result = std::make_shared( pCell, pTrack, buttonRect ); + result = AssignUIHandlePtr(holder, result); return { HitPreview(), - &Instance() + result }; } else diff --git a/src/tracks/ui/TrackButtonHandles.h b/src/tracks/ui/TrackButtonHandles.h index a6a795865..2f7fb9a78 100644 --- a/src/tracks/ui/TrackButtonHandles.h +++ b/src/tracks/ui/TrackButtonHandles.h @@ -12,6 +12,7 @@ Paul Licameli split from TrackPanel.cpp #define __AUDACITY_TRACK_BUTTON_HANDLES__ #include "../ui/ButtonHandle.h" +#include "../../TrackPanel.h" class wxMouseState; struct HitTestResult; @@ -19,11 +20,6 @@ struct HitTestResult; class MinimizeButtonHandle final : public ButtonHandle { MinimizeButtonHandle(const MinimizeButtonHandle&) = delete; - MinimizeButtonHandle &operator=(const MinimizeButtonHandle&) = delete; - - MinimizeButtonHandle(); - virtual ~MinimizeButtonHandle(); - static MinimizeButtonHandle& Instance(); protected: Result CommitChanges @@ -31,7 +27,16 @@ protected: override; public: - static HitTestResult HitTest(const wxMouseState &state, const wxRect &rect); + explicit MinimizeButtonHandle + ( const std::shared_ptr &pTrack, const wxRect &rect ); + + MinimizeButtonHandle &operator=(const MinimizeButtonHandle&) = default; + + virtual ~MinimizeButtonHandle(); + + static HitTestResult HitTest + (std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, TrackPanelCell *pCell); }; //////////////////////////////////////////////////////////////////////////////// @@ -39,11 +44,6 @@ public: class CloseButtonHandle final : public ButtonHandle { CloseButtonHandle(const CloseButtonHandle&) = delete; - CloseButtonHandle &operator=(const CloseButtonHandle&) = delete; - - CloseButtonHandle(); - virtual ~CloseButtonHandle(); - static CloseButtonHandle& Instance(); protected: Result CommitChanges @@ -53,7 +53,16 @@ protected: bool StopsOnKeystroke () override { return true; } public: - static HitTestResult HitTest(const wxMouseState &state, const wxRect &rect); + explicit CloseButtonHandle + ( const std::shared_ptr &pTrack, const wxRect &rect ); + + CloseButtonHandle &operator=(const CloseButtonHandle&) = default; + + virtual ~CloseButtonHandle(); + + static HitTestResult HitTest + (std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, TrackPanelCell *pCell); }; //////////////////////////////////////////////////////////////////////////////// @@ -64,11 +73,6 @@ public: class MenuButtonHandle final : public ButtonHandle { MenuButtonHandle(const MenuButtonHandle&) = delete; - MenuButtonHandle &operator=(const MenuButtonHandle&) = delete; - - MenuButtonHandle(); - virtual ~MenuButtonHandle(); - static MenuButtonHandle& Instance(); protected: Result CommitChanges @@ -76,8 +80,17 @@ protected: override; public: + explicit MenuButtonHandle + ( const std::shared_ptr &pCell, + const std::shared_ptr &pTrack, const wxRect &rect ); + + MenuButtonHandle &operator=(const MenuButtonHandle&) = default; + + virtual ~MenuButtonHandle(); + static HitTestResult HitTest - (const wxMouseState &state, const wxRect &rect, + (std::weak_ptr &holder, + const wxMouseState &state, const wxRect &rect, const std::shared_ptr &pCell); private: diff --git a/src/tracks/ui/TrackControls.cpp b/src/tracks/ui/TrackControls.cpp index 58507e4a9..a959f4aef 100644 --- a/src/tracks/ui/TrackControls.cpp +++ b/src/tracks/ui/TrackControls.cpp @@ -45,18 +45,24 @@ HitTestResult TrackControls::HitTest const wxRect &rect = st.rect; HitTestResult result; - if (NULL != (result = CloseButtonHandle::HitTest(state, rect)).handle) + auto pTrack = FindTrack(); + // shared pointer to this: + auto sThis = pTrack->GetTrackControl(); + + if (NULL != (result = CloseButtonHandle::HitTest( + mCloseHandle, state, rect, this)).handle) return result; - if (NULL != (result = MenuButtonHandle::HitTest(state, rect, - this->FindTrack()->GetTrackControl())).handle) + if (NULL != (result = MenuButtonHandle::HitTest( + mMenuHandle, state, rect, sThis)).handle) return result; - if (NULL != (result = MinimizeButtonHandle::HitTest(state, rect)).handle) + if (NULL != (result = MinimizeButtonHandle::HitTest( + mMinimizeHandle, state, rect, this)).handle) return result; - return TrackSelectHandle::HitAnywhere - (project->GetTrackPanel()->GetTrackCount()); + return TrackSelectHandle::HitAnywhere( + mSelectHandle, pTrack, project->GetTrackPanel()->GetTrackCount()); } enum diff --git a/src/tracks/ui/TrackControls.h b/src/tracks/ui/TrackControls.h index 348ef5792..67a32388d 100644 --- a/src/tracks/ui/TrackControls.h +++ b/src/tracks/ui/TrackControls.h @@ -17,6 +17,11 @@ Paul Licameli split from TrackPanel.cpp class PopupMenuTable; class Track; +class CloseButtonHandle; +class MenuButtonHandle; +class MinimizeButtonHandle; +class TrackSelectHandle; + class TrackControls /* not final */ : public CommonTrackPanelCell { public: @@ -54,6 +59,11 @@ protected: Track *GetTrack() const; std::weak_ptr mwTrack; + + std::weak_ptr mCloseHandle; + std::weak_ptr mMenuHandle; + std::weak_ptr mMinimizeHandle; + std::weak_ptr mSelectHandle; }; #endif diff --git a/src/tracks/ui/TrackSelectHandle.cpp b/src/tracks/ui/TrackSelectHandle.cpp index a802788b7..8d04e2dbf 100644 --- a/src/tracks/ui/TrackSelectHandle.cpp +++ b/src/tracks/ui/TrackSelectHandle.cpp @@ -26,16 +26,6 @@ Paul Licameli split from TrackPanel.cpp #include "../../../images/Cursors.h" -TrackSelectHandle::TrackSelectHandle() -{ -} - -TrackSelectHandle &TrackSelectHandle::Instance() -{ - static TrackSelectHandle instance; - return instance; -} - #if defined(__WXMAC__) /* i18n-hint: Command names a modifier key on Macintosh keyboards */ #define CTRL_CLICK _("Command-Click") @@ -59,6 +49,10 @@ namespace { } } +TrackSelectHandle::TrackSelectHandle( const std::shared_ptr &pTrack ) + : mpTrack( pTrack ) +{} + HitTestPreview TrackSelectHandle::HitPreview(unsigned trackCount) { static wxCursor arrowCursor{ wxCURSOR_ARROW }; @@ -68,11 +62,15 @@ HitTestPreview TrackSelectHandle::HitPreview(unsigned trackCount) }; } -HitTestResult TrackSelectHandle::HitAnywhere(unsigned trackCount) +HitTestResult TrackSelectHandle::HitAnywhere +(std::weak_ptr &holder, + const std::shared_ptr &pTrack, unsigned trackCount) { + auto result = std::make_shared(pTrack); + result = AssignUIHandlePtr(holder, result); return { HitPreview(trackCount), - &Instance() + result }; } @@ -97,8 +95,7 @@ UIHandle::Result TrackSelectHandle::Click if (!event.Button(wxMOUSE_BTN_LEFT)) return Cancelled; - const auto pControls = static_cast(evt.pCell.get()); - const auto pTrack = pControls->FindTrack(); + const auto pTrack = mpTrack; if (!pTrack) return Cancelled; TrackPanel *const trackPanel = pProject->GetTrackPanel(); @@ -113,7 +110,6 @@ UIHandle::Result TrackSelectHandle::Click result |= Cancelled; else { mRearrangeCount = 0; - mpTrack = pTrack; CalculateRearrangingThresholds(event); } diff --git a/src/tracks/ui/TrackSelectHandle.h b/src/tracks/ui/TrackSelectHandle.h index 555565150..6301e1737 100644 --- a/src/tracks/ui/TrackSelectHandle.h +++ b/src/tracks/ui/TrackSelectHandle.h @@ -20,14 +20,17 @@ class Track; class TrackSelectHandle final : public UIHandle { - TrackSelectHandle(); TrackSelectHandle(const TrackSelectHandle&) = delete; - TrackSelectHandle &operator=(const TrackSelectHandle&) = delete; - static TrackSelectHandle& Instance(); static HitTestPreview HitPreview(unsigned trackCount); public: - static HitTestResult HitAnywhere(unsigned trackCount); + explicit TrackSelectHandle( const std::shared_ptr &pTrack ); + + TrackSelectHandle &operator=(const TrackSelectHandle&) = default; + + static HitTestResult HitAnywhere + (std::weak_ptr &holder, + const std::shared_ptr &pTrack, unsigned trackCount); virtual ~TrackSelectHandle(); diff --git a/src/tracks/ui/TrackUI.cpp b/src/tracks/ui/TrackUI.cpp index ece5605f9..5f3d85229 100644 --- a/src/tracks/ui/TrackUI.cpp +++ b/src/tracks/ui/TrackUI.cpp @@ -23,6 +23,7 @@ Paul Licameli split from TrackPanel.cpp #include "ZoomHandle.h" #include "TimeShiftHandle.h" #include "../../TrackPanelResizerCell.h" +#include "BackgroundCell.h" HitTestResult Track::HitTest (const TrackPanelMouseState &st, @@ -35,7 +36,8 @@ HitTestResult Track::HitTest if ( !isMultiTool && currentTool == zoomTool ) // Zoom tool is a non-selecting tool that takes precedence in all tracks // over all other tools, no matter what detail you point at. - return ZoomHandle::HitAnywhere(st.state, pProject); + return ZoomHandle::HitAnywhere( + pProject->GetBackgroundCell()->mZoomHandle, st.state, pProject); // In other tools, let subclasses determine detailed hits. HitTestResult result = @@ -49,16 +51,19 @@ HitTestResult Track::HitTest // Sliding applies in more than one track type. if ( !result.handle && !isMultiTool && currentTool == slideTool ) - result = TimeShiftHandle::HitAnywhere(pProject); + result = TimeShiftHandle::HitAnywhere( + mTimeShiftHandle, pProject, Pointer(this), false); // Let the multi-tool right-click handler apply only in default of all // other detailed hits. if ( !result.handle && isMultiTool ) - result = ZoomHandle::HitTest(st.state, pProject); + result = ZoomHandle::HitTest( + pProject->GetBackgroundCell()->mZoomHandle, st.state, pProject); // Finally, default of all is adjustment of the selection box. if ( !result.handle && ( isMultiTool || currentTool == selectTool) ) - result = SelectHandle::HitTest(st, pProject, Pointer(this)); + result = SelectHandle::HitTest( + mSelectHandle, st, pProject, Pointer(this)); result.preview.refreshCode |= refresh; return result; diff --git a/src/tracks/ui/TrackVRulerControls.cpp b/src/tracks/ui/TrackVRulerControls.cpp index 03d8333f5..4b462caa9 100644 --- a/src/tracks/ui/TrackVRulerControls.cpp +++ b/src/tracks/ui/TrackVRulerControls.cpp @@ -33,11 +33,11 @@ std::shared_ptr TrackVRulerControls::FindTrack() } HitTestResult TrackVRulerControls::HitTest - (const TrackPanelMouseState &, const AudacityProject *) +(const TrackPanelMouseState &, const AudacityProject *) { // Use a space for the tip, otherwise we get the default message. static wxCursor arrowCursor{ wxCURSOR_ARROW }; - return { { _(" "), &arrowCursor }, nullptr }; + return { { _(" "), &arrowCursor }, {} }; } void TrackVRulerControls::DrawZooming diff --git a/src/tracks/ui/ZoomHandle.cpp b/src/tracks/ui/ZoomHandle.cpp index 686513ecc..9cf47bf29 100644 --- a/src/tracks/ui/ZoomHandle.cpp +++ b/src/tracks/ui/ZoomHandle.cpp @@ -38,14 +38,7 @@ Paul Licameli split from TrackPanel.cpp /// and forcing a refresh. ZoomHandle::ZoomHandle() -{ -} - -ZoomHandle &ZoomHandle::Instance() -{ - static ZoomHandle instance; - return instance; -} +{} HitTestPreview ZoomHandle::HitPreview (const wxMouseState &state, const AudacityProject *pProject) @@ -62,16 +55,20 @@ HitTestPreview ZoomHandle::HitPreview } HitTestResult ZoomHandle::HitAnywhere -(const wxMouseState &state, const AudacityProject *pProject) +(std::weak_ptr &holder, + const wxMouseState &state, const AudacityProject *pProject) { - return { HitPreview(state, pProject), &Instance() }; + auto result = std::make_shared(); + result = AssignUIHandlePtr(holder, result); + return { HitPreview(state, pProject), result }; } HitTestResult ZoomHandle::HitTest -(const wxMouseState &state, const AudacityProject *pProject) +(std::weak_ptr &holder, + const wxMouseState &state, const AudacityProject *pProject) { if (state.ButtonIsDown(wxMOUSE_BTN_RIGHT)) - return HitAnywhere(state, pProject); + return HitAnywhere(holder, state, pProject); else return {}; } diff --git a/src/tracks/ui/ZoomHandle.h b/src/tracks/ui/ZoomHandle.h index ae346c247..1ac0e3949 100644 --- a/src/tracks/ui/ZoomHandle.h +++ b/src/tracks/ui/ZoomHandle.h @@ -18,20 +18,24 @@ class wxMouseState; struct HitTestResult; +// This handle class, unlike most, doesn't associate with any particular cell. class ZoomHandle final : public UIHandle { - ZoomHandle(); ZoomHandle(const ZoomHandle&) = delete; - ZoomHandle &operator=(const ZoomHandle&) = delete; - static ZoomHandle& Instance(); static HitTestPreview HitPreview (const wxMouseState &state, const AudacityProject *pProject); public: + ZoomHandle(); + + ZoomHandle &operator=(const ZoomHandle&) = default; + static HitTestResult HitAnywhere - (const wxMouseState &state, const AudacityProject *pProject); + (std::weak_ptr &holder, + const wxMouseState &state, const AudacityProject *pProject); static HitTestResult HitTest - (const wxMouseState &state, const AudacityProject *pProject); + (std::weak_ptr &holder, + const wxMouseState &state, const AudacityProject *pProject); virtual ~ZoomHandle();