2015-07-13 23:32:26 +00:00
|
|
|
/**********************************************************************
|
|
|
|
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
|
|
|
|
CutlineHandle.cpp
|
|
|
|
|
|
|
|
Paul Licameli split from TrackPanel.cpp
|
|
|
|
|
|
|
|
**********************************************************************/
|
|
|
|
|
|
|
|
#include "../../../../Audacity.h"
|
|
|
|
#include "CutlineHandle.h"
|
2018-11-11 02:40:37 +00:00
|
|
|
|
2017-06-22 19:17:39 +00:00
|
|
|
#include "../../../../Experimental.h"
|
2015-07-13 23:32:26 +00:00
|
|
|
|
|
|
|
#include "../../../../HitTestResult.h"
|
|
|
|
#include "../../../../Project.h"
|
|
|
|
#include "../../../../RefreshCode.h"
|
2017-07-15 18:12:32 +00:00
|
|
|
#include "../../../../Snap.h" // for kPixelTolerance
|
2015-07-13 23:32:26 +00:00
|
|
|
#include "../../../../TrackPanelMouseEvent.h"
|
|
|
|
#include "../../../../UndoManager.h"
|
2019-04-28 10:49:47 +00:00
|
|
|
#include "../../../../ViewInfo.h"
|
2015-07-13 23:32:26 +00:00
|
|
|
#include "../../../../WaveTrack.h"
|
|
|
|
#include "../../../../../images/Cursors.h"
|
|
|
|
|
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.
2017-07-05 20:45:55 +00:00
|
|
|
CutlineHandle::CutlineHandle
|
|
|
|
( const std::shared_ptr<WaveTrack> &pTrack, WaveTrackLocation location )
|
|
|
|
: mpTrack{ pTrack }
|
|
|
|
, mLocation{ location }
|
2017-06-22 19:17:39 +00:00
|
|
|
{
|
2017-07-12 04:28:49 +00:00
|
|
|
}
|
|
|
|
|
2017-07-12 17:56:16 +00:00
|
|
|
void CutlineHandle::Enter(bool)
|
2017-07-12 04:28:49 +00:00
|
|
|
{
|
2017-06-22 19:17:39 +00:00
|
|
|
#ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
|
|
|
|
mChangeHighlight = RefreshCode::RefreshCell;
|
|
|
|
#endif
|
|
|
|
}
|
2015-07-13 23:32:26 +00:00
|
|
|
|
|
|
|
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")
|
2017-06-29 00:31:29 +00:00
|
|
|
: _("Left-Click to merge clips")),
|
2015-07-13 23:32:26 +00:00
|
|
|
(unsafe
|
|
|
|
? &*disabledCursor
|
|
|
|
: &arrowCursor)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
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,
|
2017-06-29 03:21:20 +00:00
|
|
|
const wxRect &rect, const wxMouseState &state,
|
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.
2017-07-05 20:45:55 +00:00
|
|
|
WaveTrackLocation *pmLocation)
|
2015-07-13 23:32:26 +00:00
|
|
|
{
|
|
|
|
for (auto loc: track->GetCachedLocations())
|
|
|
|
{
|
|
|
|
const double x = viewInfo.TimeToPosition(loc.pos);
|
|
|
|
if (x >= 0 && x < rect.width)
|
|
|
|
{
|
|
|
|
wxRect locRect;
|
2017-07-15 18:12:32 +00:00
|
|
|
locRect.width = 2 * kPixelTolerance - 1;
|
|
|
|
locRect.x = (int)(rect.x + x) - locRect.width / 2;
|
2015-07-13 23:32:26 +00:00
|
|
|
locRect.y = rect.y;
|
|
|
|
locRect.height = rect.height;
|
2017-06-29 03:21:20 +00:00
|
|
|
if (locRect.Contains(state.m_x, state.m_y))
|
2015-07-13 23:32:26 +00:00
|
|
|
{
|
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.
2017-07-05 20:45:55 +00:00
|
|
|
if (pmLocation)
|
|
|
|
*pmLocation = loc;
|
2015-07-13 23:32:26 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-04 04:48:09 +00:00
|
|
|
UIHandlePtr CutlineHandle::HitTest
|
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.
2017-07-05 20:45:55 +00:00
|
|
|
(std::weak_ptr<CutlineHandle> &holder,
|
2017-07-04 04:48:09 +00:00
|
|
|
const wxMouseState &state, const wxRect &rect,
|
|
|
|
const AudacityProject *pProject,
|
2017-06-27 17:40:36 +00:00
|
|
|
const std::shared_ptr<WaveTrack> &pTrack)
|
2015-07-13 23:32:26 +00:00
|
|
|
{
|
2019-04-28 10:49:47 +00:00
|
|
|
auto &viewInfo = ViewInfo::Get( *pProject );
|
2015-07-13 23:32:26 +00:00
|
|
|
/// method that tells us if the mouse event landed on an
|
|
|
|
/// editable Cutline
|
|
|
|
|
|
|
|
WaveTrackLocation location;
|
2017-08-12 05:07:12 +00:00
|
|
|
if (!IsOverCutline(viewInfo, pTrack.get(), rect, state, &location))
|
2015-07-13 23:32:26 +00:00
|
|
|
return {};
|
|
|
|
|
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.
2017-07-05 20:45:55 +00:00
|
|
|
auto result = std::make_shared<CutlineHandle>( pTrack, location );
|
|
|
|
result = AssignUIHandlePtr( holder, result );
|
2017-07-04 04:48:09 +00:00
|
|
|
return result;
|
2015-07-13 23:32:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CutlineHandle::~CutlineHandle()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
UIHandle::Result CutlineHandle::Click
|
|
|
|
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
|
|
|
|
{
|
2017-06-18 04:32:30 +00:00
|
|
|
using namespace RefreshCode;
|
|
|
|
const bool unsafe = pProject->IsAudioActive();
|
|
|
|
if ( unsafe )
|
|
|
|
return Cancelled;
|
|
|
|
|
2015-07-13 23:32:26 +00:00
|
|
|
const wxMouseEvent &event = evt.event;
|
2019-04-28 10:49:47 +00:00
|
|
|
auto &viewInfo = ViewInfo::Get( *pProject );
|
2015-07-13 23:32:26 +00:00
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
/// Someone has just clicked the mouse. What do we do?
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
if (event.LeftDown())
|
|
|
|
{
|
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.
2017-07-05 20:45:55 +00:00
|
|
|
if (mLocation.typ == WaveTrackLocation::locationCutLine)
|
2015-07-13 23:32:26 +00:00
|
|
|
{
|
|
|
|
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;
|
2017-01-03 22:07:12 +00:00
|
|
|
double *pCutlineStart = &cutlineStart, *pCutlineEnd = &cutlineEnd;
|
|
|
|
|
|
|
|
for (auto channel :
|
|
|
|
TrackList::Channels(mpTrack.get())) {
|
|
|
|
channel->ExpandCutLine(
|
|
|
|
mLocation.pos, pCutlineStart, pCutlineEnd);
|
|
|
|
if ( channel == mpTrack.get() )
|
|
|
|
pCutlineStart = pCutlineEnd = nullptr;
|
|
|
|
}
|
2015-07-13 23:32:26 +00:00
|
|
|
|
|
|
|
viewInfo.selectedRegion.setTimes(cutlineStart, cutlineEnd);
|
|
|
|
result |= UpdateSelection;
|
|
|
|
}
|
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.
2017-07-05 20:45:55 +00:00
|
|
|
else if (mLocation.typ == WaveTrackLocation::locationMergePoint) {
|
|
|
|
const double pos = mLocation.pos;
|
2017-01-03 22:07:12 +00:00
|
|
|
for (auto channel :
|
|
|
|
TrackList::Channels(mpTrack.get())) {
|
2015-07-13 23:32:26 +00:00
|
|
|
// Don't assume correspondence of merge points across channels!
|
2017-01-03 22:07:12 +00:00
|
|
|
int idx = FindMergeLine(channel, pos);
|
2015-07-13 23:32:26 +00:00
|
|
|
if (idx >= 0) {
|
2017-01-03 22:07:12 +00:00
|
|
|
WaveTrack::Location location =
|
|
|
|
channel->GetCachedLocations()[idx];
|
|
|
|
channel->MergeClips(
|
|
|
|
location.clipidx1, location.clipidx2);
|
2015-07-13 23:32:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mOperation = Merge;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (event.RightDown())
|
|
|
|
{
|
2017-01-03 22:07:12 +00:00
|
|
|
bool removed = false;
|
|
|
|
for (auto channel :
|
|
|
|
TrackList::Channels(mpTrack.get()))
|
|
|
|
removed = channel->RemoveCutLine(mLocation.pos) || removed;
|
2015-07-13 23:32:26 +00:00
|
|
|
|
|
|
|
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
|
2017-06-18 04:32:30 +00:00
|
|
|
(const TrackPanelMouseState &, const AudacityProject *pProject)
|
2015-07-13 23:32:26 +00:00
|
|
|
{
|
2017-06-18 04:32:30 +00:00
|
|
|
const bool unsafe = pProject->IsAudioActive();
|
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.
2017-07-05 20:45:55 +00:00
|
|
|
auto bCutline = (mLocation.typ == WaveTrackLocation::locationCutLine);
|
|
|
|
return HitPreview( bCutline, unsafe );
|
2015-07-13 23:32:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2019-04-28 10:49:47 +00:00
|
|
|
AudacityProject &project = *pProject;
|
|
|
|
auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
|
2019-03-27 08:15:28 +00:00
|
|
|
selectedRegion.setTimes( mStartTime, mEndTime );
|
2015-07-13 23:32:26 +00:00
|
|
|
result |= UpdateSelection;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|