Audacity  2.2.2
CutlineHandle.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3 Audacity: A Digital Audio Editor
4 
5 CutlineHandle.cpp
6 
7 Paul Licameli split from TrackPanel.cpp
8 
9 **********************************************************************/
10 
11 #include "../../../../Audacity.h"
12 #include "CutlineHandle.h"
13 #include "../../../../Experimental.h"
14 
15 #include "../../../../MemoryX.h"
16 
17 #include "../../../../HitTestResult.h"
18 #include "../../../../Project.h"
19 #include "../../../../RefreshCode.h"
20 #include "../../../../Snap.h" // for kPixelTolerance
21 #include "../../../../TrackPanelMouseEvent.h"
22 #include "../../../../UndoManager.h"
23 #include "../../../../WaveTrack.h"
24 #include "../../../../WaveTrackLocation.h"
25 #include "../../../../../images/Cursors.h"
26 
28 ( const std::shared_ptr<WaveTrack> &pTrack, WaveTrackLocation location )
29  : mpTrack{ pTrack }
30  , mLocation{ location }
31 {
32 }
33 
35 {
36 #ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
38 #endif
39 }
40 
41 HitTestPreview CutlineHandle::HitPreview(bool cutline, bool unsafe)
42 {
43  static auto disabledCursor =
44  ::MakeCursor(wxCURSOR_NO_ENTRY, DisabledCursorXpm, 16, 16);
45  static wxCursor arrowCursor{ wxCURSOR_ARROW };
46  return {
47  (cutline
48  ? _("Left-Click to expand, Right-Click to remove")
49  : _("Left-Click to merge clips")),
50  (unsafe
51  ? &*disabledCursor
52  : &arrowCursor)
53  };
54 }
55 namespace
56 {
57  int FindMergeLine(WaveTrack *track, double time)
58  {
59  const double tolerance = 0.5 / track->GetRate();
60  int ii = 0;
61  for (const auto loc: track->GetCachedLocations()) {
63  fabs(time - loc.pos) < tolerance)
64  return ii;
65  ++ii;
66  }
67  return -1;
68  }
69 
70  bool IsOverCutline
71  (const ViewInfo &viewInfo, WaveTrack * track,
72  const wxRect &rect, const wxMouseState &state,
73  WaveTrackLocation *pmLocation)
74  {
75  for (auto loc: track->GetCachedLocations())
76  {
77  const double x = viewInfo.TimeToPosition(loc.pos);
78  if (x >= 0 && x < rect.width)
79  {
80  wxRect locRect;
81  locRect.width = 2 * kPixelTolerance - 1;
82  locRect.x = (int)(rect.x + x) - locRect.width / 2;
83  locRect.y = rect.y;
84  locRect.height = rect.height;
85  if (locRect.Contains(state.m_x, state.m_y))
86  {
87  if (pmLocation)
88  *pmLocation = loc;
89  return true;
90  }
91  }
92  }
93 
94  return false;
95  }
96 }
97 
99 (std::weak_ptr<CutlineHandle> &holder,
100  const wxMouseState &state, const wxRect &rect,
101  const AudacityProject *pProject,
102  const std::shared_ptr<WaveTrack> &pTrack)
103 {
104  const ViewInfo &viewInfo = pProject->GetViewInfo();
107  if (pTrack->GetKind() != Track::Wave)
108  return {};
109 
110  WaveTrack *wavetrack = pTrack.get();
111  WaveTrackLocation location;
112  if (!IsOverCutline(viewInfo, wavetrack, rect, state, &location))
113  return {};
114 
115  auto result = std::make_shared<CutlineHandle>( pTrack, location );
116  result = AssignUIHandlePtr( holder, result );
117  return result;
118 }
119 
121 {
122 }
123 
125 (const TrackPanelMouseEvent &evt, AudacityProject *pProject)
126 {
127  using namespace RefreshCode;
128  const bool unsafe = pProject->IsAudioActive();
129  if ( unsafe )
130  return Cancelled;
131 
132  const wxMouseEvent &event = evt.event;
133  ViewInfo &viewInfo = pProject->GetViewInfo();
134 
135  // Can affect the track by merging clips, expanding a cutline, or
136  // deleting a cutline.
137  // All the change is done at button-down. Button-up just commits the undo item.
138 
140 
141  // FIXME: Disable this and return true when CutLines aren't showing?
142  // (Don't use gPrefs-> for the fix as registry access is slow).
143 
144  // Cutline data changed on either branch, so refresh the track display.
145  UIHandle::Result result = RefreshCell;
146  // Assume linked track is wave or null
147  const auto linked = static_cast<WaveTrack*>(mpTrack->GetLink());
148 
149  if (event.LeftDown())
150  {
151  if (mLocation.typ == WaveTrackLocation::locationCutLine)
152  {
153  mOperation = Expand;
154  mStartTime = viewInfo.selectedRegion.t0();
155  mEndTime = viewInfo.selectedRegion.t1();
156 
157  // When user presses left button on cut line, expand the line again
158  double cutlineStart = 0, cutlineEnd = 0;
159 
160  mpTrack->ExpandCutLine(mLocation.pos, &cutlineStart, &cutlineEnd);
161 
162  if (linked)
163  // Expand the cutline in the opposite channel if it is present.
164  linked->ExpandCutLine(mLocation.pos);
165 
166  viewInfo.selectedRegion.setTimes(cutlineStart, cutlineEnd);
167  result |= UpdateSelection;
168  }
169  else if (mLocation.typ == WaveTrackLocation::locationMergePoint) {
170  const double pos = mLocation.pos;
171  mpTrack->MergeClips(mLocation.clipidx1, mLocation.clipidx2);
172 
173  if (linked) {
174  // Don't assume correspondence of merge points across channels!
175  int idx = FindMergeLine(linked, pos);
176  if (idx >= 0) {
177  WaveTrack::Location location = linked->GetCachedLocations()[idx];
178  linked->MergeClips(location.clipidx1, location.clipidx2);
179  }
180  }
181 
182  mOperation = Merge;
183  }
184  }
185  else if (event.RightDown())
186  {
187  bool removed = mpTrack->RemoveCutLine(mLocation.pos);
188 
189  if (linked)
190  removed = linked->RemoveCutLine(mLocation.pos) || removed;
191 
192  if (!removed)
193  // Nothing happened, make no Undo item
194  return Cancelled;
195 
196  mOperation = Remove;
197  }
198  else
199  result = RefreshNone;
200 
201  return result;
202 }
203 
206 {
208 }
209 
211 (const TrackPanelMouseState &, const AudacityProject *pProject)
212 {
213  const bool unsafe = pProject->IsAudioActive();
214  auto bCutline = (mLocation.typ == WaveTrackLocation::locationCutLine);
215  return HitPreview( bCutline, unsafe );
216 }
217 
219 (const TrackPanelMouseEvent &, AudacityProject *pProject, wxWindow *)
220 {
222 
223  // Only now commit the result to the undo stack
224  AudacityProject *const project = pProject;
225  switch (mOperation) {
226  default:
227  wxASSERT(false);
228  case Merge:
229  project->PushState(_("Merged Clips"), _("Merge"), UndoPush::CONSOLIDATE);
230  break;
231  case Expand:
232  project->PushState(_("Expanded Cut Line"), _("Expand"));
234  break;
235  case Remove:
236  project->PushState(_("Removed Cut Line"), _("Remove"));
237  break;
238  }
239 
240  // Nothing to do for the display
241  return result;
242 }
243 
245 {
246  using namespace RefreshCode;
247  UIHandle::Result result = RefreshCell;
248  pProject->RollbackState();
249  if (mOperation == Expand) {
250  AudacityProject *const project = pProject;
251  project->SetSel0(mStartTime);
252  project->SetSel1(mEndTime);
253  result |= UpdateSelection;
254  }
255  return result;
256 }
const std::vector< Location > & GetCachedLocations() const
Definition: WaveTrack.h:502
Operation mOperation
Definition: CutlineHandle.h:71
void SetSel1(double)
Definition: Project.cpp:1404
double t0() const
ViewInfo is used mainly to hold the zooming, selection and scroll information. It also has some statu...
Definition: ViewInfo.h:141
SelectedRegion selectedRegion
Definition: ViewInfo.h:160
void SetSel0(double)
Definition: Project.cpp:1397
double t1() const
const int kPixelTolerance
Definition: Snap.h:57
Result Release(const TrackPanelMouseEvent &event, AudacityProject *pProject, wxWindow *pParent) override
Result mChangeHighlight
Definition: UIHandle.h:150
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:176
unsigned Result
Definition: UIHandle.h:37
virtual ~CutlineHandle()
void RollbackState()
Definition: Project.cpp:4735
A Track that contains audio waveform data.
Definition: WaveTrack.h:60
double mStartTime
Definition: CutlineHandle.h:72
wxInt64 TimeToPosition(double time, wxInt64 origin=0, bool ignoreFisheye=false) const
STM: Converts a project time to screen x position.
Definition: ViewInfo.cpp:59
Result Drag(const TrackPanelMouseEvent &event, AudacityProject *pProject) override
bool IsAudioActive() const
Definition: Project.cpp:1447
static HitTestPreview HitPreview(bool cutline, bool unsafe)
std::shared_ptr< UIHandle > UIHandlePtr
Definition: TrackPanel.h:59
Used only by WaveTrack, a special way to hold location that can accommodate merged regions...
bool setTimes(double t0, double t1)
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom")).Raw()), OnMoveTrack)#define SET_TRACK_NAME_PLUGIN_SYMBOLclass SetTrackNameCommand:public AudacityCommand
HitTestPreview Preview(const TrackPanelMouseState &state, const AudacityProject *pProject) override
Result Cancel(AudacityProject *pProject) override
void PushState(const wxString &desc, const wxString &shortDesc)
Definition: Project.cpp:4695
CutlineHandle(const CutlineHandle &)=delete
std::unique_ptr< wxCursor > MakeCursor(int WXUNUSED(CursorId), const char *const pXpm[36], int HotX, int HotY)
Definition: TrackPanel.cpp:274
double GetRate() const
Definition: WaveTrack.cpp:398
void Enter(bool forward) override
const ViewInfo & GetViewInfo() const
Definition: Project.h:207
Result Click(const TrackPanelMouseEvent &event, AudacityProject *pProject) override
std::shared_ptr< Subclass > AssignUIHandlePtr(std::weak_ptr< Subclass > &holder, const std::shared_ptr< Subclass > &pNew)
Definition: UIHandle.h:162
static UIHandlePtr HitTest(std::weak_ptr< CutlineHandle > &holder, const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr< WaveTrack > &pTrack)