1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-09-15 15:50:54 +02:00

TrackPanel no longer implements the selection tool or MIDI stretch...

This one's big!

Also restores the effect of ctrl-click on label track.

Also adds ESC key handling for the Stretch.
This commit is contained in:
Paul Licameli 2015-09-08 21:15:35 -04:00 committed by Paul Licameli
parent efdb9889b1
commit 770b3b52ef
16 changed files with 2161 additions and 2087 deletions

View File

@ -1238,6 +1238,8 @@
5E7396691DAFDB5600BA0A4D /* LabelDefaultClickHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E7396631DAFDB5600BA0A4D /* LabelDefaultClickHandle.cpp */; };
5E73966A1DAFDB5600BA0A4D /* LabelGlyphHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E7396651DAFDB5600BA0A4D /* LabelGlyphHandle.cpp */; };
5E73966B1DAFDB5600BA0A4D /* LabelTextHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E7396671DAFDB5600BA0A4D /* LabelTextHandle.cpp */; };
5E73966E1DAFDB8500BA0A4D /* StretchHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E73966C1DAFDB8500BA0A4D /* StretchHandle.cpp */; };
5E7396711DAFDB9D00BA0A4D /* SelectHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E73966F1DAFDB9D00BA0A4D /* SelectHandle.cpp */; };
5E74D2E31CC4429700D88B0B /* EditCursorOverlay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E74D2DD1CC4429700D88B0B /* EditCursorOverlay.cpp */; };
5E74D2E41CC4429700D88B0B /* PlayIndicatorOverlay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E74D2DF1CC4429700D88B0B /* PlayIndicatorOverlay.cpp */; };
5E74D2E51CC4429700D88B0B /* Scrubbing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E74D2E11CC4429700D88B0B /* Scrubbing.cpp */; };
@ -3085,6 +3087,10 @@
5E7396661DAFDB5600BA0A4D /* LabelGlyphHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LabelGlyphHandle.h; sourceTree = "<group>"; };
5E7396671DAFDB5600BA0A4D /* LabelTextHandle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LabelTextHandle.cpp; sourceTree = "<group>"; };
5E7396681DAFDB5600BA0A4D /* LabelTextHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LabelTextHandle.h; sourceTree = "<group>"; };
5E73966C1DAFDB8500BA0A4D /* StretchHandle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StretchHandle.cpp; sourceTree = "<group>"; };
5E73966D1DAFDB8500BA0A4D /* StretchHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StretchHandle.h; sourceTree = "<group>"; };
5E73966F1DAFDB9D00BA0A4D /* SelectHandle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SelectHandle.cpp; sourceTree = "<group>"; };
5E7396701DAFDB9D00BA0A4D /* SelectHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SelectHandle.h; sourceTree = "<group>"; };
5E74D2D91CC4427B00D88B0B /* TrackPanelCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackPanelCell.h; sourceTree = "<group>"; };
5E74D2DA1CC4427B00D88B0B /* TrackPanelCellIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackPanelCellIterator.h; sourceTree = "<group>"; };
5E74D2DD1CC4429700D88B0B /* EditCursorOverlay.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EditCursorOverlay.cpp; sourceTree = "<group>"; };
@ -5797,6 +5803,7 @@
5E7396451DAFD8F200BA0A4D /* EnvelopeHandle.cpp */,
5E74D2DF1CC4429700D88B0B /* PlayIndicatorOverlay.cpp */,
5E74D2E11CC4429700D88B0B /* Scrubbing.cpp */,
5E73966F1DAFDB9D00BA0A4D /* SelectHandle.cpp */,
5E73964E1DAFD98400BA0A4D /* SliderHandle.cpp */,
5E7396421DAFD8C600BA0A4D /* TimeShiftHandle.cpp */,
5E7396571DAFDA3600BA0A4D /* TrackButtonHandles.cpp */,
@ -5812,6 +5819,7 @@
5E7396461DAFD8F200BA0A4D /* EnvelopeHandle.h */,
5E74D2E01CC4429700D88B0B /* PlayIndicatorOverlay.h */,
5E74D2E21CC4429700D88B0B /* Scrubbing.h */,
5E7396701DAFDB9D00BA0A4D /* SelectHandle.h */,
5E73964F1DAFD98400BA0A4D /* SliderHandle.h */,
5E7396431DAFD8C600BA0A4D /* TimeShiftHandle.h */,
5E7396581DAFDA3600BA0A4D /* TrackButtonHandles.h */,
@ -5851,6 +5859,8 @@
5EA0181E1EC7B226001F2996 /* NoteTrackUI.cpp */,
5EA0181F1EC7B226001F2996 /* NoteTrackVRulerControls.cpp */,
5EA018201EC7B226001F2996 /* NoteTrackVRulerControls.h */,
5E73966C1DAFDB8500BA0A4D /* StretchHandle.cpp */,
5E73966D1DAFDB8500BA0A4D /* StretchHandle.h */,
);
path = ui;
sourceTree = "<group>";
@ -7657,6 +7667,7 @@
1790B15109883BFD008A330A /* StereoToMono.cpp in Sources */,
1790B15209883BFD008A330A /* ToneGen.cpp in Sources */,
5E74D2E31CC4429700D88B0B /* EditCursorOverlay.cpp in Sources */,
5E7396711DAFDB9D00BA0A4D /* SelectHandle.cpp in Sources */,
1790B15309883BFD008A330A /* TruncSilence.cpp in Sources */,
1790B15409883BFD008A330A /* TwoPassSimpleMono.cpp in Sources */,
1790B15809883BFD008A330A /* Wahwah.cpp in Sources */,
@ -7787,6 +7798,7 @@
283B3D4D0BC21EBE00FA01D5 /* FileDialog.cpp in Sources */,
2809C4B80BCB7E560006010F /* FileIO.cpp in Sources */,
285DE1FA0BF03C7800A20DF0 /* Screenshot.cpp in Sources */,
5E73966E1DAFDB8500BA0A4D /* StretchHandle.cpp in Sources */,
2801A6460BF9268700648258 /* ImportQT.cpp in Sources */,
2891B2870C531D2C0044FBE3 /* FindClipping.cpp in Sources */,
283AA0EB0C56ED08002CBD34 /* ErrorDialog.cpp in Sources */,

View File

@ -569,6 +569,8 @@ audacity_SOURCES = \
tracks/playabletrack/notetrack/ui/NoteTrackUI.cpp \
tracks/playabletrack/notetrack/ui/NoteTrackVRulerControls.cpp \
tracks/playabletrack/notetrack/ui/NoteTrackVRulerControls.h \
tracks/playabletrack/notetrack/ui/StretchHandle.cpp \
tracks/playabletrack/notetrack/ui/StretchHandle.h \
tracks/playabletrack/ui/PlayableTrackButtonHandles.cpp \
tracks/playabletrack/ui/PlayableTrackButtonHandles.h \
tracks/playabletrack/wavetrack/ui/CutlineHandle.cpp \
@ -601,6 +603,8 @@ audacity_SOURCES = \
tracks/ui/PlayIndicatorOverlay.h \
tracks/ui/Scrubbing.cpp \
tracks/ui/Scrubbing.h \
tracks/ui/SelectHandle.cpp \
tracks/ui/SelectHandle.h \
tracks/ui/SliderHandle.cpp \
tracks/ui/SliderHandle.h \
tracks/ui/TimeShiftHandle.cpp \

View File

@ -104,6 +104,9 @@ simplifies construction of menu items.
#include "toolbars/DeviceToolBar.h"
#include "toolbars/MixerToolBar.h"
#include "toolbars/TranscriptionToolBar.h"
#include "tracks/ui/SelectHandle.h"
#include "widgets/LinkingHtmlWindow.h"
#include "Experimental.h"
@ -5879,7 +5882,7 @@ void AudacityProject::DoNextPeakFrequency(bool up)
}
if (pTrack) {
mTrackPanel->SnapCenterOnce(pTrack, up);
SelectHandle::Instance().SnapCenterOnce(mViewInfo, pTrack, up);
mTrackPanel->Refresh(false);
ModifyState(false);
}

View File

@ -2224,6 +2224,8 @@ void AudacityProject::OnToolBarUpdate(wxCommandEvent & event)
// The projects tracklist has been updated
void AudacityProject::OnTrackListUpdated(wxCommandEvent & event)
{
GetSelectionState().TrackListUpdated( *GetTracks() );
mViewInfo.track = NULL;
event.Skip();

File diff suppressed because it is too large Load Diff

View File

@ -336,115 +336,27 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
virtual MixerBoard* GetMixerBoard();
virtual bool IsAudioActive();
virtual bool IsUnsafe();
virtual void HandleTrackSpecificMouseEvent(wxMouseEvent & event);
virtual void ScrollDuringDrag();
// Working out where to dispatch the event to.
virtual int DetermineToolToUse( ToolsToolBar * pTtb, const wxMouseEvent & event);
#ifdef USE_MIDI
// data for NoteTrack interactive stretch operations:
// Stretching applies to a selected region after quantizing the
// region to beat boundaries (subbeat stretching is not supported,
// but maybe it should be enabled with shift or ctrl or something)
// Stretching can drag the left boundary (the right stays fixed),
// the right boundary (the left stays fixed), or the center (splits
// the selection into two parts: when left part grows, the right
// part shrinks, keeping the leftmost and rightmost boundaries
// fixed.
enum StretchEnum {
stretchNone = 0, // false value!
stretchLeft,
stretchCenter,
stretchRight
};
struct StretchState {
StretchEnum mMode { stretchCenter }; // remembers what to drag
using QuantizedTimeAndBeat = std::pair< double, double >;
bool mStretching {}; // true between mouse down and mouse up
double mOrigT0 {};
double mOrigT1 {};
QuantizedTimeAndBeat mBeatCenter { 0, 0 };
QuantizedTimeAndBeat mBeat0 { 0, 0 };
QuantizedTimeAndBeat mBeat1 { 0, 0 };
double mLeftBeats {}; // how many beats from left to cursor
double mRightBeats {}; // how many beats from cursor to right
} mStretchState;
virtual StretchEnum HitTestStretch
( const Track *track, const wxRect &rect, const wxMouseEvent & event,
StretchState *pState = nullptr );
wxCursor *ChooseStretchCursor( StretchEnum mode );
static StretchEnum ChooseStretchMode
( const wxMouseEvent &event, const wxRect &rect, const ViewInfo &viewInfo,
const NoteTrack *nt, StretchState *pState = nullptr );
virtual void Stretch(int mouseXCoordinate, int trackLeftEdge, Track *pTrack);
#endif
// AS: Selection handling
public:
size_t GetTrackCount() const;
size_t GetSelectedTrackCount() const;
protected:
virtual void HandleSelect(wxMouseEvent & event);
virtual void SelectionHandleDrag(wxMouseEvent &event, Track *pTrack);
virtual void SelectionHandleClick(wxMouseEvent &event,
Track* pTrack, wxRect rect);
virtual void StartSelection (int mouseXCoordinate, int trackLeftEdge);
virtual void ExtendSelection(int mouseXCoordinate, int trackLeftEdge,
Track *pTrack);
virtual void UpdateSelectionDisplay();
public:
virtual void UpdateAccessibility();
void MessageForScreenReader(const wxString& message);
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
public:
void SnapCenterOnce (const WaveTrack *pTrack, bool up);
protected:
void StartSnappingFreqSelection (const WaveTrack *pTrack);
void MoveSnappingFreqSelection (int mouseYCoordinate,
int trackTopEdge,
int trackHeight, Track *pTrack);
void StartFreqSelection (int mouseYCoordinate, int trackTopEdge,
int trackHeight, Track *pTrack);
void ExtendFreqSelection(int mouseYCoordinate, int trackTopEdge,
int trackHeight);
void ResetFreqSelectionPin(double hintFrequency, bool logF);
#endif
protected:
// AS: Cursor handling
virtual bool SetCursorByActivity( );
virtual void SetCursorAndTipWhenSelectTool
( Track * t, const wxMouseEvent & event, const wxRect &rect, bool bMultiToolMode, wxString &tip, const wxCursor ** ppCursor );
virtual void SetCursorAndTipByTool( int tool, const wxMouseEvent & event, wxString &tip );
public:
virtual void HandleCursor(wxMouseEvent & event);
protected:
virtual void MaySetOnDemandTip( Track * t, wxString &tip );
// MM: Handle mouse wheel rotation
virtual void HandleWheelRotation(wxMouseEvent & event);
public:
virtual void MakeParentRedrawScrollbars();
protected:
// AS: Pushing the state preserves state for Undo operations.
virtual void MakeParentPushState(const wxString &desc, const wxString &shortDesc); // use UndoPush::AUTOSAVE
virtual void MakeParentPushState(const wxString &desc, const wxString &shortDesc,
UndoPush flags);
virtual void MakeParentModifyState(bool bWantsAutoSave); // if true, writes auto-save file. Should set only if you really want the state change restored after
// a crash, as it can take many seconds for large (eg. 10 track-hours) projects
@ -558,42 +470,12 @@ protected:
bool mRefreshBacking;
SelectedRegion mInitialSelection;
SelectionState &GetSelectionState();
std::unique_ptr<SelectionStateChanger> mSelectionStateChanger{};
bool mSelStartValid;
double mSelStart;
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
enum eFreqSelMode {
FREQ_SEL_INVALID,
FREQ_SEL_SNAPPING_CENTER,
FREQ_SEL_PINNED_CENTER,
FREQ_SEL_DRAG_CENTER,
FREQ_SEL_FREE,
FREQ_SEL_TOP_FREE,
FREQ_SEL_BOTTOM_FREE,
} mFreqSelMode;
// Following holds:
// the center for FREQ_SEL_PINNED_CENTER,
// the ratio of top to center (== center to bottom) for FREQ_SEL_DRAG_CENTER,
// a frequency boundary for FREQ_SEL_FREE, FREQ_SEL_TOP_FREE, or
// FREQ_SEL_BOTTOM_FREE,
// and is ignored otherwise.
double mFreqSelPin;
const WaveTrack *mFreqSelTrack = NULL;
std::unique_ptr<SpectrumAnalyst> mFrequencySnapper;
protected:
#endif
Track *mCapturedTrack;
wxRect mCapturedRect;
bool mRedrawAfterStop;
wxMouseEvent mLastMouseEvent;
@ -601,72 +483,13 @@ protected:
int mMouseMostRecentX;
int mMouseMostRecentY;
// Handles snapping the selection boundaries or track boundaries to
// line up with existing tracks or labels. mSnapLeft and mSnapRight
// are the horizontal index of pixels to display user feedback
// guidelines so the user knows when such snapping is taking place.
std::unique_ptr<SnapManager> mSnapManager;
wxInt64 mSnapLeft { -1 };
wxInt64 mSnapRight { -1 };
public:
wxInt64 GetSnapLeft () const
{
return mSnapLeft ;
}
wxInt64 GetSnapRight() const
{
return mSnapRight;
}
protected:
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
void HandleCenterFrequencyCursor
(bool shiftDown, wxString &tip, const wxCursor ** ppCursor);
void HandleCenterFrequencyClick
(bool shiftDown, const WaveTrack *pTrack, double value);
double PositionToFrequency(const WaveTrack *wt,
bool maySnap,
wxInt64 mouseYCoordinate,
wxInt64 trackTopEdge,
int trackHeight) const;
wxInt64 FrequencyToPosition(const WaveTrack *wt,
double frequency,
wxInt64 trackTopEdge,
int trackHeight) const;
#endif
enum SelectionBoundary {
SBNone,
SBLeft, SBRight,
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
SBBottom, SBTop, SBCenter, SBWidth,
#endif
};
SelectionBoundary ChooseTimeBoundary
(double selend, bool onlyWithinSnapDistance,
wxInt64 *pPixelDist = NULL, double *pPinValue = NULL) const;
SelectionBoundary ChooseBoundary
(const wxMouseEvent & event, const Track *pTrack,
const wxRect &rect,
bool mayDragWidth,
bool onlyWithinSnapDistance,
double *pPinValue = NULL) const;
bool mAutoScrolling;
public:
// Old enumeration of click-and-drag states, which will shrink and disappear
// as UIHandle subclasses take over the repsonsibilities.
enum MouseCaptureEnum
{
IsUncaptured=0, // This is the normal state for the mouse
IsUncaptured = 0,
IsClosing,
IsSelecting,
IsMuting,
IsSoloing,
IsMinimizing,
@ -674,24 +497,6 @@ public:
};
protected:
enum MouseCaptureEnum mMouseCapture;
virtual void SetCapturedTrack( Track * t, enum MouseCaptureEnum MouseCapture=IsUncaptured );
std::unique_ptr<wxCursor>
mArrowCursor, mSelectCursor,
mEnvelopeCursor, // doubles as the center frequency cursor
// for spectral selection
mDisabledCursor, mAdjustLeftSelectionCursor, mAdjustRightSelectionCursor;
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
std::unique_ptr<wxCursor>
mBottomFrequencyCursor, mTopFrequencyCursor, mBandWidthCursor;
#endif
#ifdef USE_MIDI
std::unique_ptr<wxCursor>
mStretchCursor, mStretchLeftCursor, mStretchRightCursor;
#endif
friend class TrackPanelAx;
#if wxUSE_ACCESSIBILITY
@ -707,8 +512,6 @@ protected:
static wxString gSoloPref;
protected:
// The screenshot class needs to access internals
friend class ScreenshotCommand;
@ -765,11 +568,4 @@ enum : int {
//the bottom of a track that can be used for vertical track resizing.
#define TRACK_RESIZE_REGION 5
//This constant determines the size of the horizontal region (in pixels) around
//the right and left selection bounds that can be used for horizontal selection adjusting
//(or, vertical distance around top and bottom bounds in spectrograms,
// for vertical selection adjusting)
#define SELECTION_RESIZE_REGION 3
#endif

View File

@ -15,8 +15,12 @@ Paul Licameli split from TrackPanel.cpp
#include "LabelGlyphHandle.h"
#include "LabelTextHandle.h"
#include "../../ui/SelectHandle.h"
#include "../../../HitTestResult.h"
#include "../../../Project.h"
#include "../../../TrackPanelMouseEvent.h"
#include "../../../toolbars/ToolsToolBar.h"
HitTestResult LabelTrack::HitTest
(const TrackPanelMouseEvent &evt,
@ -37,6 +41,12 @@ HitTestResult LabelTrack::HitTest
// Missed glyph, try text box
// This hit test does not define its own messages or cursor
HitTestResult defaultResult = Track::HitTest(evt, pProject);
if (!defaultResult.handle) {
// In case of multi tool, default to selection.
const ToolsToolBar *const pTtb = pProject->GetToolsToolBar();
if (pTtb->IsDown(multiTool))
defaultResult = SelectHandle::HitTest(evt, pProject, this);
}
result = LabelTextHandle::HitTest(event, this);
if (result.handle)
// Use any cursor or status message change from catchall,

View File

@ -17,12 +17,49 @@ Paul Licameli split from TrackPanel.cpp
#include "NoteTrackVRulerControls.h"
#include "../../../../HitTestResult.h"
#include "../../../../Project.h"
#include "../../../../TrackPanelMouseEvent.h"
#include "../../../ui/SelectHandle.h"
#include "StretchHandle.h"
#include "../../../../toolbars/ToolsToolBar.h"
HitTestResult NoteTrack::HitTest
(const TrackPanelMouseEvent &event,
const AudacityProject *pProject)
{
return Track::HitTest(event, pProject);
const ToolsToolBar *const pTtb = pProject->GetToolsToolBar();
// If the cursor is in the hot zone for stretching, that takes precedence
// over selection, but it didn't take precedence over other hits.
// That was the old logic, and maybe I tried too hard to preserve it just so.
// PRL.
// Eligible for stretch?
HitTestResult result1;
#ifdef USE_MIDI
StretchHandle::StretchState state;
result1 = StretchHandle::HitTest( event, pProject, this, state );
#endif
// But some other non-select tool like zoom may take priority.
HitTestResult result = Track::HitTest(event, pProject);
if (result.preview.cursor &&
!(result1.preview.cursor && pTtb->GetCurrentTool() == selectTool))
return result;
if (pTtb->IsDown(multiTool)) {
// Default to selection
if (!result1.preview.cursor &&
NULL != (result =
SelectHandle::HitTest(event, pProject, this)).preview.cursor)
return result;
}
// Do stretch!
if (result1.preview.cursor)
return result1;
return result;
}
TrackControls *NoteTrack::GetControls()

View File

@ -0,0 +1,339 @@
/**********************************************************************
Audacity: A Digital Audio Editor
StretchHandle.cpp
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#include "../../../../Audacity.h"
#ifdef USE_MIDI
#include "StretchHandle.h"
#include "../../../../HitTestResult.h"
#include "../../../../NoteTrack.h"
#include "../../../../Project.h"
#include "../../../../RefreshCode.h"
#include "../../../../TrackPanelMouseEvent.h"
#include "../../../../UndoManager.h"
#include "../../../../../images/Cursors.h"
#include <algorithm>
StretchHandle::StretchHandle()
{
}
StretchHandle &StretchHandle::Instance()
{
static StretchHandle instance;
return instance;
}
HitTestPreview StretchHandle::HitPreview( StretchEnum stretchMode, bool unsafe )
{
static auto disabledCursor =
::MakeCursor(wxCURSOR_NO_ENTRY, DisabledCursorXpm, 16, 16);
static auto stretchLeftCursor =
::MakeCursor(wxCURSOR_BULLSEYE, StretchLeftCursorXpm, 16, 16);
static auto stretchRightCursor =
::MakeCursor(wxCURSOR_BULLSEYE, StretchRightCursorXpm, 16, 16);
static auto stretchCursor =
::MakeCursor(wxCURSOR_BULLSEYE, StretchCursorXpm, 16, 16);
if (unsafe) {
return { _(""), &*disabledCursor };
}
else {
wxCursor *pCursor = NULL;
switch (stretchMode) {
default:
wxASSERT(false);
case stretchLeft:
pCursor = &*stretchLeftCursor; break;
case stretchCenter:
pCursor = &*stretchCursor; break;
case stretchRight:
pCursor = &*stretchRightCursor; break;
}
return {
_("Click and drag to stretch selected region."),
pCursor
};
}
}
HitTestResult StretchHandle::HitTest
( const TrackPanelMouseEvent &evt, const AudacityProject *pProject,
NoteTrack *pTrack, StretchState &stretchState)
{
const wxMouseEvent &event = evt.event;
// later, we may want a different policy, but for now, stretch is
// selected when the cursor is near the center of the track and
// within the selection
const ViewInfo &viewInfo = pProject->GetViewInfo();
const bool unsafe = pProject->IsAudioActive();
if (!pTrack || !pTrack->GetSelected() || pTrack->GetKind() != Track::Note)
return {};
const wxRect &rect = evt.rect;
int center = rect.y + rect.height / 2;
int distance = abs(event.m_y - center);
const int yTolerance = 10;
wxInt64 leftSel = viewInfo.TimeToPosition(viewInfo.selectedRegion.t0(), rect.x);
wxInt64 rightSel = viewInfo.TimeToPosition(viewInfo.selectedRegion.t1(), rect.x);
// Something is wrong if right edge comes before left edge
wxASSERT(!(rightSel < leftSel));
if (!(leftSel <= event.m_x && event.m_x <= rightSel &&
distance < yTolerance))
return {};
// find nearest beat to sel0, sel1
static const double minPeriod = 0.05; // minimum beat period
stretchState.mBeatCenter = { 0, 0 };
auto t0 = viewInfo.selectedRegion.t0();
stretchState.mBeat0 = pTrack->NearestBeatTime( t0 );
stretchState.mOrigSel0Quantized = stretchState.mBeat0.first;
auto t1 = viewInfo.selectedRegion.t1();
stretchState.mBeat1 = pTrack->NearestBeatTime( t1 );
stretchState.mOrigSel1Quantized = stretchState.mBeat1.first;
// If there is not (almost) a beat to stretch that is slower
// than 20 beats per second, don't stretch
if ( within( stretchState.mBeat0.second,
stretchState.mBeat1.second, 0.9 ) ||
( stretchState.mBeat1.first - stretchState.mBeat0.first ) /
( stretchState.mBeat1.second - stretchState.mBeat0.second )
< minPeriod )
return {};
auto selStart = viewInfo.PositionToTime( event.m_x, rect.x );
stretchState.mBeatCenter = pTrack->NearestBeatTime( selStart );
bool startNewSelection = true;
if ( within( stretchState.mBeat0.second,
stretchState.mBeatCenter.second, 0.1 ) ) {
stretchState.mMode = stretchLeft;
stretchState.mLeftBeats = 0;
stretchState.mRightBeats =
stretchState.mBeat1.second - stretchState.mBeat0.second;
}
else if ( within( stretchState.mBeat1.second,
stretchState.mBeatCenter.second, 0.1 ) ) {
stretchState.mMode = stretchRight;
stretchState.mLeftBeats =
stretchState.mBeat1.second - stretchState.mBeat0.second;
stretchState.mRightBeats = 0;
}
else {
stretchState.mMode = stretchCenter;
stretchState.mLeftBeats =
stretchState.mBeat1.second - stretchState.mBeatCenter.second;
stretchState.mRightBeats =
stretchState.mBeatCenter.second - stretchState.mBeat0.second;
}
return {
HitPreview( stretchState.mMode, unsafe ),
&Instance()
};
}
StretchHandle::~StretchHandle()
{
}
UIHandle::Result StretchHandle::Click
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
using namespace RefreshCode;
const bool unsafe = pProject->IsAudioActive();
const wxMouseEvent &event = evt.event;
if (unsafe ||
event.LeftDClick() ||
!event.LeftDown() ||
evt.pCell == NULL)
return Cancelled;
mLeftEdge = evt.rect.GetLeft();
mpTrack = static_cast<NoteTrack*>(evt.pCell);
ViewInfo &viewInfo = pProject->GetViewInfo();
// We must have hit if we got here, but repeat some
// calculations that set members
HitTest( evt, pProject, mpTrack, mStretchState );
viewInfo.selectedRegion.setTimes
( mStretchState.mBeat0.first, mStretchState.mBeat1.first );
// Full refresh since the label area may need to indicate
// newly selected tracks. (I'm really not sure if the label area
// needs to be refreshed or how to just refresh non-label areas.-RBD)
return RefreshAll | UpdateSelection;
}
UIHandle::Result StretchHandle::Drag
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
using namespace RefreshCode;
const bool unsafe = pProject->IsAudioActive();
if (unsafe) {
this->Cancel(pProject);
return RefreshAll | Cancelled;
}
const wxMouseEvent &event = evt.event;
const int x = event.m_x;
Track *clickedTrack =
static_cast<CommonTrackPanelCell*>(evt.pCell)->FindTrack();
if (clickedTrack == NULL && mpTrack != NULL)
clickedTrack = mpTrack;
Stretch(pProject, x, mLeftEdge, clickedTrack);
return RefreshAll;
}
HitTestPreview StretchHandle::Preview
(const TrackPanelMouseEvent &, const AudacityProject *pProject)
{
const bool unsafe = pProject->IsAudioActive();
return HitPreview( mStretchState.mMode, unsafe );
}
UIHandle::Result StretchHandle::Release
(const TrackPanelMouseEvent &, AudacityProject *pProject,
wxWindow *)
{
using namespace RefreshCode;
const bool unsafe = pProject->IsAudioActive();
if (unsafe) {
this->Cancel(pProject);
return RefreshAll | Cancelled;
}
bool left;
ViewInfo &viewInfo = pProject->GetViewInfo();
if ( pProject->IsSyncLocked() &&
( ( left = mStretchState.mMode == stretchLeft ) ||
mStretchState.mMode == stretchRight ) ) {
SyncLockedTracksIterator syncIter( pProject->GetTracks() );
for ( auto track = syncIter.StartWith( mpTrack ); track != nullptr;
track = syncIter.Next() ) {
if ( track != mpTrack ) {
if ( left ) {
auto origT0 = mStretchState.mOrigSel0Quantized;
auto diff = viewInfo.selectedRegion.t0() - origT0;
if ( diff > 0)
track->SyncLockAdjust( origT0 + diff, origT0 );
else
track->SyncLockAdjust( origT0, origT0 - diff );
track->Offset( diff );
}
else {
auto origT1 = mStretchState.mOrigSel1Quantized;
auto diff = viewInfo.selectedRegion.t1() - origT1;
track->SyncLockAdjust( origT1, origT1 + diff );
}
}
}
}
/* i18n-hint: (noun) The track that is used for MIDI notes which can be
dragged to change their duration.*/
pProject->PushState(_("Stretch Note Track"),
/* i18n-hint: In the history list, indicates a MIDI note has
been dragged to change its duration (stretch it). Using either past
or present tense is fine here. If unsure, go for whichever is
shorter.*/
_("Stretch"),
UndoPush::CONSOLIDATE | UndoPush::AUTOSAVE);
return RefreshAll;
}
UIHandle::Result StretchHandle::Cancel(AudacityProject *pProject)
{
pProject->RollbackState();
return RefreshCode::RefreshNone;
}
void StretchHandle::Stretch(AudacityProject *pProject, int mouseXCoordinate, int trackLeftEdge,
Track *pTrack)
{
ViewInfo &viewInfo = pProject->GetViewInfo();
TrackList *const trackList = pProject->GetTracks();
if (pTrack == NULL && mpTrack != NULL)
pTrack = mpTrack;
if (!pTrack || pTrack->GetKind() != Track::Note) {
return;
}
NoteTrack *pNt = static_cast<NoteTrack *>(pTrack);
double moveto =
std::max(0.0, viewInfo.PositionToTime(mouseXCoordinate, trackLeftEdge));
auto t1 = viewInfo.selectedRegion.t1();
auto t0 = viewInfo.selectedRegion.t0();
double dur, left_dur, right_dur;
// check to make sure tempo is not higher than 20 beats per second
// (In principle, tempo can be higher, but not infinity.)
double minPeriod = 0.05; // minimum beat period
// make sure target duration is not too short
// Take quick exit if so, without changing the selection.
switch ( mStretchState.mMode ) {
case stretchLeft: {
dur = t1 - moveto;
if (dur < mStretchState.mRightBeats * minPeriod)
return;
pNt->StretchRegion
( mStretchState.mBeat0, mStretchState.mBeat1, dur );
pNt->Offset( moveto - t0 );
mStretchState.mBeat0.first = moveto;
viewInfo.selectedRegion.setT0(moveto);
break;
}
case stretchRight: {
dur = moveto - t0;
if (dur < mStretchState.mLeftBeats * minPeriod)
return;
pNt->StretchRegion
( mStretchState.mBeat0, mStretchState.mBeat1, dur );
viewInfo.selectedRegion.setT1(moveto);
mStretchState.mBeat1.first = moveto;
viewInfo.selectedRegion.setT0(moveto);
break;
}
case stretchCenter: {
left_dur = moveto - t0;
right_dur = t1 - moveto;
if ( left_dur < mStretchState.mLeftBeats * minPeriod ||
right_dur < mStretchState.mRightBeats * minPeriod )
return;
pNt->StretchRegion
( mStretchState.mBeatCenter, mStretchState.mBeat1, right_dur );
pNt->StretchRegion
( mStretchState.mBeat0, mStretchState.mBeatCenter, left_dur );
mStretchState.mBeatCenter.first = moveto;
break;
}
default:
wxASSERT(false);
break;
}
}
#endif

View File

@ -0,0 +1,98 @@
/**********************************************************************
Audacity: A Digital Audio Editor
StretchHandle.h
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#ifndef __AUDACITY_STRETCH_HANDLE__
#define __AUDACITY_STRETCH_HANDLE__
#include "../../../../UIHandle.h"
#include "../../../../MemoryX.h"
class Alg_seq;
struct HitTestResult;
class NoteTrack;
class Track;
class ViewInfo;
class wxCursor;
class StretchHandle : public UIHandle
{
public:
enum StretchEnum {
stretchNone = 0, // false value!
stretchLeft,
stretchCenter,
stretchRight
};
// Stretching applies to a selected region after quantizing the
// region to beat boundaries (subbeat stretching is not supported,
// but maybe it should be enabled with shift or ctrl or something)
// Stretching can drag the left boundary (the right stays fixed),
// the right boundary (the left stays fixed), or the center (splits
// the selection into two parts: when left part grows, the right
// part shrinks, keeping the leftmost and rightmost boundaries
// fixed.
struct StretchState {
StretchEnum mMode { stretchCenter }; // remembers what to drag
using QuantizedTimeAndBeat = std::pair< double, double >;
QuantizedTimeAndBeat mBeatCenter { 0, 0 };
QuantizedTimeAndBeat mBeat0 { 0, 0 };
QuantizedTimeAndBeat mBeat1 { 0, 0 };
double mLeftBeats {}; // how many beats from left to cursor
double mRightBeats {}; // how many beats from cursor to right
double mOrigSel0Quantized { -1 }, mOrigSel1Quantized { -1 };
};
private:
StretchHandle();
StretchHandle(const StretchHandle&);
StretchHandle &operator=(const StretchHandle&);
static StretchHandle& Instance();
static HitTestPreview HitPreview(StretchEnum stretchMode, bool unsafe);
public:
static HitTestResult HitTest
( const TrackPanelMouseEvent &event, const AudacityProject *pProject,
NoteTrack *pTrack, StretchState &state );
virtual ~StretchHandle();
virtual Result Click
(const TrackPanelMouseEvent &event, AudacityProject *pProject);
virtual Result Drag
(const TrackPanelMouseEvent &event, AudacityProject *pProject);
virtual HitTestPreview Preview
(const TrackPanelMouseEvent &event, const AudacityProject *pProject);
virtual Result Release
(const TrackPanelMouseEvent &event, AudacityProject *pProject,
wxWindow *pParent);
virtual Result Cancel(AudacityProject *pProject);
bool StopsOnKeystroke() override { return true; }
private:
void Stretch
(AudacityProject *pProject, int mouseXCoordinate, int trackLeftEdge, Track *pTrack);
NoteTrack *mpTrack{};
int mLeftEdge{ -1 };
StretchState mStretchState{};
};
#endif

View File

@ -18,6 +18,7 @@ Paul Licameli split from TrackPanel.cpp
#include "../../../../toolbars/ToolsToolBar.h"
#include "CutlineHandle.h"
#include "../../../ui/SelectHandle.h"
#include "../../../ui/EnvelopeHandle.h"
#include "SampleHandle.h"
#include "../../../ui/TimeShiftHandle.h"
@ -26,6 +27,11 @@ HitTestResult WaveTrack::HitTest
(const TrackPanelMouseEvent &event,
const AudacityProject *pProject)
{
// FIXME: Should similar logic apply to NoteTrack (#if defined(USE_MIDI)) ?
// From here on the order in which we hit test determines
// which tool takes priority in the rare cases where it
// could be more than one.
// This hit was always tested first no matter which tool:
HitTestResult result = CutlineHandle::HitTest(event.event, event.rect, pProject, this);
if (result.preview.cursor)
@ -51,6 +57,10 @@ HitTestResult WaveTrack::HitTest
else if (NULL != (result =
SampleHandle::HitTest(event.event, event.rect, pProject, this)).preview.cursor)
;
else if (NULL != (result =
SelectHandle::HitTest(event, pProject, this)).preview.cursor)
// default of all other hit tests
;
}
return result;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,153 @@
/**********************************************************************
Audacity: A Digital Audio Editor
SelectHandle.h
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#ifndef __AUDACITY_SELECT_HANDLE__
#define __AUDACITY_SELECT_HANDLE__
#include "../../UIHandle.h"
#include "../../SelectedRegion.h"
#include "../../MemoryX.h"
#include <vector>
#include <wx/event.h>
#include <wx/gdicmn.h>
struct HitTestResult;
class SelectionStateChanger;
class SnapManager;
class SpectrumAnalyst;
class Track;
class ViewInfo;
class WaveTrack;
class SelectHandle : public wxEvtHandler, public UIHandle
{
SelectHandle();
SelectHandle(const SelectHandle&);
SelectHandle &operator=(const SelectHandle&);
public:
static SelectHandle& Instance();
// This always hits, but details of the hit vary with mouse position and
// key state.
static HitTestResult HitTest
(const TrackPanelMouseEvent &event, const AudacityProject *pProject, const Track *pTrack);
virtual ~SelectHandle();
virtual Result Click
(const TrackPanelMouseEvent &event, AudacityProject *pProject);
virtual Result Drag
(const TrackPanelMouseEvent &event, AudacityProject *pProject);
virtual HitTestPreview Preview
(const TrackPanelMouseEvent &event, const AudacityProject *pProject);
virtual Result Release
(const TrackPanelMouseEvent &event, AudacityProject *pProject,
wxWindow *pParent);
virtual Result Cancel(AudacityProject*);
virtual void DrawExtras
(DrawingPass pass,
wxDC * dc, const wxRegion &updateRegion, const wxRect &panelRect);
void OnProjectChange(AudacityProject *pProject) override;
// Receives timer event notifications, to implement auto-scroll
void OnTimer(wxCommandEvent &event);
private:
void Connect(AudacityProject *pProject);
void Disconnect();
void StartSelection
(AudacityProject *pProject, int mouseXCoordinate, int trackLeftEdge);
void AdjustSelection
(ViewInfo &viewInfo, int mouseXCoordinate, int trackLeftEdge,
Track *pTrack);
void StartFreqSelection
(ViewInfo &viewInfo, int mouseYCoordinate, int trackTopEdge,
int trackHeight, Track *pTrack);
void AdjustFreqSelection
(ViewInfo &viewInfo, int mouseYCoordinate, int trackTopEdge,
int trackHeight);
void HandleCenterFrequencyClick
(const ViewInfo &viewInfo, bool shiftDown,
const WaveTrack *pTrack, double value);
void StartSnappingFreqSelection
(const ViewInfo &viewInfo, const WaveTrack *pTrack);
void MoveSnappingFreqSelection
(AudacityProject *pProject, ViewInfo &viewInfo, int mouseYCoordinate,
int trackTopEdge,
int trackHeight, Track *pTrack);
public:
// This is needed to implement a command assignable to keystrokes
void SnapCenterOnce
(ViewInfo &viewInfo, const WaveTrack *pTrack, bool up);
private:
//void ResetFreqSelectionPin
// (const ViewInfo &viewInfo, double hintFrequency, bool logF);
Track *mpTrack;
wxRect mRect;
SelectedRegion mInitialSelection;
// Handles snapping the selection boundaries or track boundaries to
// line up with existing tracks or labels. mSnapLeft and mSnapRight
// are the horizontal index of pixels to display user feedback
// guidelines so the user knows when such snapping is taking place.
std::unique_ptr<SnapManager> mSnapManager;
wxInt64 mSnapLeft;
wxInt64 mSnapRight;
bool mSelStartValid;
double mSelStart;
int mSelectionBoundary;
enum eFreqSelMode {
FREQ_SEL_INVALID,
FREQ_SEL_SNAPPING_CENTER,
FREQ_SEL_PINNED_CENTER,
FREQ_SEL_DRAG_CENTER,
FREQ_SEL_FREE,
FREQ_SEL_TOP_FREE,
FREQ_SEL_BOTTOM_FREE,
} mFreqSelMode;
const WaveTrack *mFreqSelTrack {};
// Following holds:
// the center for FREQ_SEL_PINNED_CENTER,
// the ratio of top to center (== center to bottom) for FREQ_SEL_DRAG_CENTER,
// a frequency boundary for FREQ_SEL_FREE, FREQ_SEL_TOP_FREE, or
// FREQ_SEL_BOTTOM_FREE,
// and is ignored otherwise.
double mFreqSelPin;
std::unique_ptr<SpectrumAnalyst> mFrequencySnapper;
int mMostRecentX, mMostRecentY;
bool mAutoScrolling;
AudacityProject *mConnectedProject;
std::unique_ptr<SelectionStateChanger> mSelectionStateChanger;
};
#endif

View File

@ -17,6 +17,7 @@ Paul Licameli split from TrackPanel.cpp
#include "../../Project.h"
#include "../../toolbars/ToolsToolBar.h"
#include "../ui/SelectHandle.h"
#include "EnvelopeHandle.h"
#include "../playabletrack/wavetrack/ui/SampleHandle.h"
#include "ZoomHandle.h"
@ -40,10 +41,10 @@ HitTestResult Track::HitTest
return ZoomHandle::HitAnywhere(event.event, pProject);
case slideTool:
return TimeShiftHandle::HitAnywhere(pProject);
case selectTool:
return SelectHandle::HitTest(event, pProject, this);
default:
// cases not yet implemented
// fallthru
;
}

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
@ -234,6 +234,7 @@
<ClCompile Include="..\..\..\src\tracks\playabletrack\notetrack\ui\NoteTrackControls.cpp" />
<ClCompile Include="..\..\..\src\tracks\playabletrack\notetrack\ui\NoteTrackUI.cpp" />
<ClCompile Include="..\..\..\src\tracks\playabletrack\notetrack\ui\NoteTrackVRulerControls.cpp" />
<ClCompile Include="..\..\..\src\tracks\playabletrack\notetrack\ui\StretchHandle.cpp" />
<ClCompile Include="..\..\..\src\tracks\playabletrack\ui\PlayableTrackButtonHandles.cpp" />
<ClCompile Include="..\..\..\src\tracks\playabletrack\wavetrack\ui\CutlineHandle.cpp" />
<ClCompile Include="..\..\..\src\tracks\playabletrack\wavetrack\ui\SampleHandle.cpp" />
@ -251,6 +252,7 @@
<ClCompile Include="..\..\..\src\tracks\ui\EnvelopeHandle.cpp" />
<ClCompile Include="..\..\..\src\tracks\ui\PlayIndicatorOverlay.cpp" />
<ClCompile Include="..\..\..\src\tracks\ui\Scrubbing.cpp" />
<ClCompile Include="..\..\..\src\tracks\ui\SelectHandle.cpp" />
<ClCompile Include="..\..\..\src\tracks\ui\SliderHandle.cpp" />
<ClCompile Include="..\..\..\src\tracks\ui\TimeShiftHandle.cpp" />
<ClCompile Include="..\..\..\src\tracks\ui\TrackButtonHandles.cpp" />
@ -505,6 +507,7 @@
<ClInclude Include="..\..\..\src\TrackPanelMouseEvent.h" />
<ClInclude Include="..\..\..\src\tracks\playabletrack\notetrack\ui\NoteTrackControls.h" />
<ClInclude Include="..\..\..\src\tracks\playabletrack\notetrack\ui\NoteTrackVRulerControls.h" />
<ClInclude Include="..\..\..\src\tracks\playabletrack\notetrack\ui\StretchHandle.h" />
<ClInclude Include="..\..\..\src\tracks\playabletrack\ui\PlayableTrackButtonHandles.h" />
<ClInclude Include="..\..\..\src\tracks\playabletrack\wavetrack\ui\CutlineHandle.h" />
<ClInclude Include="..\..\..\src\tracks\playabletrack\wavetrack\ui\SampleHandle.h" />
@ -515,6 +518,7 @@
<ClInclude Include="..\..\..\src\tracks\labeltrack\ui\LabelDefaultClickHandle.h" />
<ClInclude Include="..\..\..\src\tracks\labeltrack\ui\LabelGlyphHandle.h" />
<ClInclude Include="..\..\..\src\tracks\labeltrack\ui\LabelTextHandle.h" />
<ClInclude Include="..\..\..\src\tracks\ui\SelectHandle.h" />
<ClInclude Include="..\..\..\src\tracks\ui\BackgroundCell.h" />
<ClInclude Include="..\..\..\src\tracks\ui\CommonTrackPanelCell.h" />
<ClInclude Include="..\..\..\src\tracks\labeltrack\ui\LabelTrackControls.h" />
@ -1203,4 +1207,4 @@
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\ny.targets" />
</ImportGroup>
</Project>
</Project>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="src">
@ -1040,6 +1040,12 @@
<ClCompile Include="..\..\..\src\tracks\labeltrack\ui\LabelDefaultClickHandle.cpp">
<Filter>src\tracks\labeltrack\ui</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\tracks\ui\SelectHandle.cpp">
<Filter>src\tracks\ui</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\tracks\playabletrack\notetrack\ui\StretchHandle.cpp">
<Filter>src\tracks\playabletrack\notetrack\ui</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\src\AboutDialog.h">
@ -2071,6 +2077,12 @@
<ClInclude Include="..\..\..\src\tracks\labeltrack\ui\LabelDefaultClickHandle.h">
<Filter>src\tracks\labeltrack\ui</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\tracks\ui\SelectHandle.h">
<Filter>src\tracks\ui</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\tracks\playabletrack\notetrack\ui\StretchHandle.h">
<Filter>src\tracks\playabletrack\notetrack\ui</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="..\..\audacity.ico">
@ -2294,4 +2306,4 @@
<Filter>plug-ins</Filter>
</copy>
</ItemGroup>
</Project>
</Project>