mirror of
https://github.com/cookiengineer/audacity
synced 2025-11-14 17:14:07 +01:00
TrackPanel no longer implements clip joining or cutline expansion/deletion...
... also implement ESC key for these and added status bar message for mouse-over
This commit is contained in:
committed by
Paul Licameli
parent
2496b0d7bc
commit
43968c4ac9
262
src/tracks/playabletrack/wavetrack/ui/CutlineHandle.cpp
Normal file
262
src/tracks/playabletrack/wavetrack/ui/CutlineHandle.cpp
Normal file
@@ -0,0 +1,262 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
CutlineHandle.cpp
|
||||
|
||||
Paul Licameli split from TrackPanel.cpp
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "../../../../Audacity.h"
|
||||
#include "CutlineHandle.h"
|
||||
|
||||
#include "../../../../MemoryX.h"
|
||||
|
||||
#include "../../../../HitTestResult.h"
|
||||
#include "../../../../Project.h"
|
||||
#include "../../../../RefreshCode.h"
|
||||
#include "../../../../TrackPanelMouseEvent.h"
|
||||
#include "../../../../UndoManager.h"
|
||||
#include "../../../../WaveTrack.h"
|
||||
#include "../../../../WaveTrackLocation.h"
|
||||
#include "../../../../../images/Cursors.h"
|
||||
|
||||
CutlineHandle::CutlineHandle()
|
||||
{
|
||||
}
|
||||
|
||||
CutlineHandle &CutlineHandle::Instance()
|
||||
{
|
||||
static CutlineHandle instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
HitTestPreview CutlineHandle::HitPreview(bool cutline, bool unsafe)
|
||||
{
|
||||
static auto disabledCursor =
|
||||
::MakeCursor(wxCURSOR_NO_ENTRY, DisabledCursorXpm, 16, 16);
|
||||
static wxCursor arrowCursor{ wxCURSOR_ARROW };
|
||||
return {
|
||||
(cutline
|
||||
? _("Left-Click to expand, Right-Click to remove")
|
||||
: _("Left-Click to join clips")),
|
||||
(unsafe
|
||||
? &*disabledCursor
|
||||
: &arrowCursor)
|
||||
};
|
||||
}
|
||||
|
||||
HitTestResult CutlineHandle::HitAnywhere(const AudacityProject *pProject, bool cutline)
|
||||
{
|
||||
const bool unsafe = pProject->IsAudioActive();
|
||||
return {
|
||||
HitPreview(cutline, unsafe),
|
||||
(unsafe
|
||||
? NULL
|
||||
: &Instance())
|
||||
};
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
int FindMergeLine(WaveTrack *track, double time)
|
||||
{
|
||||
const double tolerance = 0.5 / track->GetRate();
|
||||
int ii = 0;
|
||||
for (const auto loc: track->GetCachedLocations()) {
|
||||
if (loc.typ == WaveTrackLocation::locationMergePoint &&
|
||||
fabs(time - loc.pos) < tolerance)
|
||||
return ii;
|
||||
++ii;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool IsOverCutline
|
||||
(const ViewInfo &viewInfo, WaveTrack * track,
|
||||
const wxRect &rect, const wxMouseEvent &event,
|
||||
WaveTrackLocation *pCapturedTrackLocation)
|
||||
{
|
||||
for (auto loc: track->GetCachedLocations())
|
||||
{
|
||||
const double x = viewInfo.TimeToPosition(loc.pos);
|
||||
if (x >= 0 && x < rect.width)
|
||||
{
|
||||
wxRect locRect;
|
||||
locRect.x = (int)(rect.x + x) - 5;
|
||||
locRect.width = 11;
|
||||
locRect.y = rect.y;
|
||||
locRect.height = rect.height;
|
||||
if (locRect.Contains(event.m_x, event.m_y))
|
||||
{
|
||||
if (pCapturedTrackLocation)
|
||||
*pCapturedTrackLocation = loc;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
HitTestResult CutlineHandle::HitTest
|
||||
(const wxMouseEvent &event, const wxRect &rect, const AudacityProject *pProject, Track *pTrack)
|
||||
{
|
||||
const ViewInfo &viewInfo = pProject->GetViewInfo();
|
||||
/// method that tells us if the mouse event landed on an
|
||||
/// editable Cutline
|
||||
if (pTrack->GetKind() != Track::Wave)
|
||||
return {};
|
||||
|
||||
WaveTrack *wavetrack = static_cast<WaveTrack*>(pTrack);
|
||||
WaveTrackLocation location;
|
||||
if (!IsOverCutline(viewInfo, wavetrack, rect, event, &location))
|
||||
return {};
|
||||
|
||||
return HitAnywhere(pProject, location.typ == WaveTrackLocation::locationCutLine);
|
||||
}
|
||||
|
||||
CutlineHandle::~CutlineHandle()
|
||||
{
|
||||
}
|
||||
|
||||
UIHandle::Result CutlineHandle::Click
|
||||
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
|
||||
{
|
||||
const wxMouseEvent &event = evt.event;
|
||||
ViewInfo &viewInfo = pProject->GetViewInfo();
|
||||
Track *const pTrack = static_cast<Track*>(evt.pCell);
|
||||
|
||||
// Can affect the track by merging clips, expanding a cutline, or
|
||||
// deleting a cutline.
|
||||
// All the change is done at button-down. Button-up just commits the undo item.
|
||||
using namespace RefreshCode;
|
||||
|
||||
/// Someone has just clicked the mouse. What do we do?
|
||||
const bool unsafe = pProject->IsAudioActive();
|
||||
if (unsafe)
|
||||
return Cancelled;
|
||||
|
||||
WaveTrackLocation capturedTrackLocation;
|
||||
|
||||
WaveTrack *wavetrack = static_cast<WaveTrack*>(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*>(wavetrack->GetLink());
|
||||
|
||||
if (event.LeftDown())
|
||||
{
|
||||
if (capturedTrackLocation.typ == WaveTrackLocation::locationCutLine)
|
||||
{
|
||||
mOperation = Expand;
|
||||
mStartTime = viewInfo.selectedRegion.t0();
|
||||
mEndTime = viewInfo.selectedRegion.t1();
|
||||
|
||||
// When user presses left button on cut line, expand the line again
|
||||
double cutlineStart = 0, cutlineEnd = 0;
|
||||
|
||||
wavetrack->ExpandCutLine(capturedTrackLocation.pos, &cutlineStart, &cutlineEnd);
|
||||
|
||||
if (linked)
|
||||
// Expand the cutline in the opposite channel if it is present.
|
||||
linked->ExpandCutLine(capturedTrackLocation.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);
|
||||
|
||||
if (linked) {
|
||||
// Don't assume correspondence of merge points across channels!
|
||||
int idx = FindMergeLine(linked, pos);
|
||||
if (idx >= 0) {
|
||||
WaveTrack::Location location = linked->GetCachedLocations()[idx];
|
||||
linked->MergeClips(location.clipidx1, location.clipidx2);
|
||||
}
|
||||
}
|
||||
|
||||
mOperation = Merge;
|
||||
}
|
||||
}
|
||||
else if (event.RightDown())
|
||||
{
|
||||
bool removed = wavetrack->RemoveCutLine(capturedTrackLocation.pos);
|
||||
|
||||
if (linked)
|
||||
removed = linked->RemoveCutLine(capturedTrackLocation.pos) || removed;
|
||||
|
||||
if (!removed)
|
||||
// Nothing happened, make no Undo item
|
||||
return Cancelled;
|
||||
|
||||
mOperation = Remove;
|
||||
}
|
||||
else
|
||||
result = RefreshNone;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
UIHandle::Result CutlineHandle::Drag
|
||||
(const TrackPanelMouseEvent &, AudacityProject *)
|
||||
{
|
||||
return RefreshCode::RefreshNone;
|
||||
}
|
||||
|
||||
HitTestPreview CutlineHandle::Preview
|
||||
(const TrackPanelMouseEvent &, const AudacityProject *)
|
||||
{
|
||||
return HitPreview(mbCutline, false);
|
||||
}
|
||||
|
||||
UIHandle::Result CutlineHandle::Release
|
||||
(const TrackPanelMouseEvent &, AudacityProject *pProject, wxWindow *)
|
||||
{
|
||||
UIHandle::Result result = RefreshCode::RefreshNone;
|
||||
|
||||
// Only now commit the result to the undo stack
|
||||
AudacityProject *const project = pProject;
|
||||
switch (mOperation) {
|
||||
default:
|
||||
wxASSERT(false);
|
||||
case Merge:
|
||||
project->PushState(_("Merged Clips"), _("Merge"), UndoPush::CONSOLIDATE);
|
||||
break;
|
||||
case Expand:
|
||||
project->PushState(_("Expanded Cut Line"), _("Expand"));
|
||||
result |= RefreshCode::UpdateSelection;
|
||||
break;
|
||||
case Remove:
|
||||
project->PushState(_("Removed Cut Line"), _("Remove"));
|
||||
break;
|
||||
}
|
||||
|
||||
// Nothing to do for the display
|
||||
return result;
|
||||
}
|
||||
|
||||
UIHandle::Result CutlineHandle::Cancel(AudacityProject *pProject)
|
||||
{
|
||||
using namespace RefreshCode;
|
||||
UIHandle::Result result = RefreshCell;
|
||||
pProject->RollbackState();
|
||||
if (mOperation == Expand) {
|
||||
AudacityProject *const project = pProject;
|
||||
project->SetSel0(mStartTime);
|
||||
project->SetSel1(mEndTime);
|
||||
result |= UpdateSelection;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
61
src/tracks/playabletrack/wavetrack/ui/CutlineHandle.h
Normal file
61
src/tracks/playabletrack/wavetrack/ui/CutlineHandle.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
CutlineHandle.h
|
||||
|
||||
Paul Licameli
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef __AUDACITY_CUTLINE_HANDLE__
|
||||
#define __AUDACITY_CUTLINE_HANDLE__
|
||||
|
||||
#include "../../../../UIHandle.h"
|
||||
|
||||
class wxMouseEvent;
|
||||
struct HitTestResult;
|
||||
class Track;
|
||||
|
||||
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);
|
||||
static HitTestResult HitTest
|
||||
(const wxMouseEvent &event, const wxRect &rect,
|
||||
const AudacityProject *pProject, Track *pTrack);
|
||||
|
||||
virtual ~CutlineHandle();
|
||||
|
||||
Result Click
|
||||
(const TrackPanelMouseEvent &event, AudacityProject *pProject) override;
|
||||
|
||||
Result Drag
|
||||
(const TrackPanelMouseEvent &event, AudacityProject *pProject) override;
|
||||
|
||||
HitTestPreview Preview
|
||||
(const TrackPanelMouseEvent &event, const AudacityProject *pProject)
|
||||
override;
|
||||
|
||||
Result Release
|
||||
(const TrackPanelMouseEvent &event, AudacityProject *pProject,
|
||||
wxWindow *pParent) override;
|
||||
|
||||
Result Cancel(AudacityProject *pProject) override;
|
||||
|
||||
bool StopsOnKeystroke() override { return true; }
|
||||
|
||||
private:
|
||||
enum Operation { Merge, Expand, Remove };
|
||||
Operation mOperation{ Merge };
|
||||
double mStartTime{}, mEndTime{};
|
||||
bool mbCutline{};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -17,6 +17,7 @@ Paul Licameli split from TrackPanel.cpp
|
||||
#include "../../../../TrackPanelMouseEvent.h"
|
||||
#include "../../../../toolbars/ToolsToolBar.h"
|
||||
|
||||
#include "CutlineHandle.h"
|
||||
#include "../../../ui/EnvelopeHandle.h"
|
||||
#include "SampleHandle.h"
|
||||
#include "../../../ui/TimeShiftHandle.h"
|
||||
@@ -25,7 +26,12 @@ HitTestResult WaveTrack::HitTest
|
||||
(const TrackPanelMouseEvent &event,
|
||||
const AudacityProject *pProject)
|
||||
{
|
||||
HitTestResult result = Track::HitTest(event, pProject);
|
||||
// This hit was always tested first no matter which tool:
|
||||
HitTestResult result = CutlineHandle::HitTest(event.event, event.rect, pProject, this);
|
||||
if (result.preview.cursor)
|
||||
return result;
|
||||
|
||||
result = Track::HitTest(event, pProject);
|
||||
if (result.preview.cursor)
|
||||
return result;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user