Audacity  3.0.3
TrackSelectHandle.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3 Audacity: A Digital Audio Editor
4 
5 TrackSelectHandle.cpp
6 
7 Paul Licameli split from TrackPanel.cpp
8 
9 **********************************************************************/
10 
11 
12 #include "TrackSelectHandle.h"
13 
14 #include "TrackView.h"
15 #include "../../HitTestResult.h"
16 #include "../../Project.h"
17 #include "../../ProjectAudioIO.h"
18 #include "../../ProjectHistory.h"
19 #include "../../RefreshCode.h"
20 #include "../../SelectUtilities.h"
21 #include "../../TrackPanelMouseEvent.h"
22 #include "../../Track.h"
23 
24 #include <wx/cursor.h>
25 #include <wx/translation.h>
26 
27 #include "../../../images/Cursors.h"
28 
29 #if defined(__WXMAC__)
30 /* i18n-hint: Command names a modifier key on Macintosh keyboards */
31 #define CTRL_CLICK XO("Command+Click")
32 #else
33 /* i18n-hint: Ctrl names a modifier key on Windows or Linux keyboards */
34 #define CTRL_CLICK XO("Ctrl+Click")
35 #endif
36 
37 namespace {
38  TranslatableString Message(unsigned trackCount) {
39  if (trackCount > 1)
40  return XO(
41 // i18n-hint: %s is replaced by (translation of) 'Ctrl+Click' on windows, 'Command+Click' on Mac
42 "%s to select or deselect track. Drag up or down to change track order.")
43  .Format( CTRL_CLICK );
44  else
45  // i18n-hint: %s is replaced by (translation of) 'Ctrl+Click' on windows, 'Command+Click' on Mac
46  return XO("%s to select or deselect track.")
47  .Format( CTRL_CLICK );
48  }
49 }
50 
51 TrackSelectHandle::TrackSelectHandle( const std::shared_ptr<Track> &pTrack )
52  : mpTrack( pTrack )
53 {}
54 
56 (std::weak_ptr<TrackSelectHandle> &holder,
57  const std::shared_ptr<Track> &pTrack)
58 {
59  auto result = std::make_shared<TrackSelectHandle>(pTrack);
60  result = AssignUIHandlePtr(holder, result);
61  return result;
62 }
63 
65 {
66 }
67 
69 (const TrackPanelMouseEvent &evt, AudacityProject *pProject)
70 {
71  // If unsafe to drag, still, it does harmlessly change the selected track
72  // set on button down.
73 
74  using namespace RefreshCode;
75  Result result = RefreshNone;
76 
77  const wxMouseEvent &event = evt.event;
78 
79  // AS: If not a click, ignore the mouse event.
80  if (!event.ButtonDown() && !event.ButtonDClick())
81  return Cancelled;
82  if (!event.Button(wxMOUSE_BTN_LEFT))
83  return Cancelled;
84 
85  const auto pTrack = mpTrack;
86  if (!pTrack)
87  return Cancelled;
88  const bool unsafe = ProjectAudioIO::Get( *pProject ).IsAudioActive();
89 
90  // DM: If they weren't clicking on a particular part of a track label,
91  // deselect other tracks and select this one.
92 
93  // JH: also, capture the current track for rearranging, so the user
94  // can drag the track up or down to swap it with others
95  if (unsafe)
96  result |= Cancelled;
97  else {
98  mRearrangeCount = 0;
99  CalculateRearrangingThresholds(event, pProject);
100  }
101 
103  pTrack.get(), event.ShiftDown(), event.ControlDown(), !unsafe);
104 
105  mClicked = true;
106  return result;
107 }
108 
110 (const TrackPanelMouseEvent &evt, AudacityProject *pProject)
111 {
112  using namespace RefreshCode;
113  Result result = RefreshNone;
114 
115  const wxMouseEvent &event = evt.event;
116 
117  auto &tracks = TrackList::Get( *pProject );
118 
119  // probably harmless during play? However, we do disallow the click, so check this too.
120  bool unsafe = ProjectAudioIO::Get( *pProject ).IsAudioActive();
121  if (unsafe)
122  return result;
123 
124  if (event.m_y < mMoveUpThreshold || event.m_y < 0) {
125  tracks.MoveUp(mpTrack.get());
126  --mRearrangeCount;
127  }
128  else if ( event.m_y > mMoveDownThreshold
129  || event.m_y > evt.whole.GetHeight() ) {
130  tracks.MoveDown(mpTrack.get());
131  ++mRearrangeCount;
132  }
133  else
134  return result;
135 
136  // JH: if we moved up or down, recalculate the thresholds and make sure the
137  // track is fully on-screen.
138  CalculateRearrangingThresholds(event, pProject);
139 
140  result |= EnsureVisible | RefreshAll;
141  return result;
142 }
143 
145 (const TrackPanelMouseState &, AudacityProject *project)
146 {
147  static auto disabledCursor =
148  ::MakeCursor(wxCURSOR_NO_ENTRY, DisabledCursorXpm, 16, 16);
149  //static wxCursor rearrangeCursor{ wxCURSOR_HAND };
150  static auto rearrangingCursor =
151  ::MakeCursor(wxCURSOR_HAND, RearrangingCursorXpm, 16, 16);
152  static wxCursor arrowCursor{ wxCURSOR_ARROW };
153 
154  //static auto hoverCursor =
155  // ::MakeCursor(wxCURSOR_HAND, RearrangeCursorXpm, 16, 16);
156  //static auto clickedCursor =
157  // ::MakeCursor(wxCURSOR_HAND, RearrangingCursorXpm, 16, 16);
158 
159  const auto trackCount = TrackList::Get( *project ).Leaders().size();
160  auto message = Message(trackCount);
161  if (mClicked) {
162  const bool unsafe =
163  ProjectAudioIO::Get( *project ).IsAudioActive();
164  const bool canMove = TrackList::Get( *project ).Leaders().size() > 1;
165  return {
166  message,
167  (unsafe
168  ? &*disabledCursor
169  : canMove
170  ? &*rearrangingCursor
171  : &arrowCursor)
172  // , message // Stop showing the tooltip after the click
173  };
174  }
175  else {
176  // Only mouse-over
177  // Don't test safety, because the click to change selection is allowed
178  return {
179  message,
180  &arrowCursor,
181  message
182  };
183  }
184 }
185 
187 (const TrackPanelMouseEvent &, AudacityProject *project, wxWindow *)
188 {
189  // If we're releasing, surely we are dragging a track?
190  wxASSERT( mpTrack );
191  if (mRearrangeCount != 0) {
192  ProjectHistory::Get( *project ).PushState(
193  /* i18n-hint: will substitute name of track for %s */
194  ( mRearrangeCount < 0 ? XO("Moved '%s' up") : XO("Moved '%s' down") )
195  .Format( mpTrack->GetName() ),
196  XO("Move Track"));
197  }
198  // Bug 1677
199  // Holding on to the reference to the track was causing it to be released far later
200  // than necessary, on shutdown, and so causing a crash as a dialog about cleaning
201  // out files could not show at that time.
202  mpTrack.reset();
203  // No need to redraw, that was done when drag moved the track
205 }
206 
208 {
209  ProjectHistory::Get( *pProject ).RollbackState();
210  // Bug 1677
211  mpTrack.reset();
213 }
214 
218  const wxMouseEvent & event, AudacityProject *project)
219 {
220  // JH: this will probably need to be tweaked a bit, I'm just
221  // not sure what formula will have the best feel for the
222  // user.
223 
224  auto &tracks = TrackList::Get( *project );
225 
226  if (tracks.CanMoveUp(mpTrack.get()))
228  event.m_y -
230  * -- tracks.FindLeader( mpTrack.get() ) );
231  else
232  mMoveUpThreshold = INT_MIN;
233 
234  if (tracks.CanMoveDown(mpTrack.get()))
236  event.m_y +
238  * ++ tracks.FindLeader( mpTrack.get() ) );
239  else
240  mMoveDownThreshold = INT_MAX;
241 }
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
TrackSelectHandle::mMoveDownThreshold
int mMoveDownThreshold
Definition: TrackSelectHandle.h:59
anonymous_namespace{TrackSelectHandle.cpp}::Message
TranslatableString Message(unsigned trackCount)
Definition: TrackSelectHandle.cpp:38
RefreshCode::RefreshAll
@ RefreshAll
Definition: RefreshCode.h:26
CTRL_CLICK
#define CTRL_CLICK
Definition: TrackSelectHandle.cpp:34
RefreshCode::RefreshNone
@ RefreshNone
Definition: RefreshCode.h:21
TrackSelectHandle::Click
Result Click(const TrackPanelMouseEvent &event, AudacityProject *pProject) override
Definition: TrackSelectHandle.cpp:69
TrackView.h
TrackSelectHandle::mpTrack
std::shared_ptr< Track > mpTrack
Definition: TrackSelectHandle.h:53
Format
Abstract base class used in importing a file.
MakeCursor
std::unique_ptr< wxCursor > MakeCursor(int WXUNUSED(CursorId), const char *const pXpm[36], int HotX, int HotY)
Definition: TrackPanel.cpp:176
TrackSelectHandle::mRearrangeCount
int mRearrangeCount
Definition: TrackSelectHandle.h:60
TrackSelectHandle::mClicked
bool mClicked
Definition: TrackSelectHandle.h:54
RefreshCode::Cancelled
@ Cancelled
Definition: RefreshCode.h:23
XO
#define XO(s)
Definition: Internat.h:31
TrackSelectHandle::Release
Result Release(const TrackPanelMouseEvent &event, AudacityProject *pProject, wxWindow *pParent) override
Definition: TrackSelectHandle.cpp:187
TrackPanelMouseEvent::whole
const wxSize & whole
Definition: TrackPanelMouseEvent.h:60
ProjectAudioIO::Get
static ProjectAudioIO & Get(AudacityProject &project)
Definition: ProjectAudioIO.cpp:23
RefreshCode::EnsureVisible
@ EnsureVisible
Definition: RefreshCode.h:31
ProjectAudioIO::IsAudioActive
bool IsAudioActive() const
Definition: ProjectAudioIO.cpp:52
TrackSelectHandle::TrackSelectHandle
TrackSelectHandle(const TrackSelectHandle &)=delete
TrackSelectHandle::HitAnywhere
static UIHandlePtr HitAnywhere(std::weak_ptr< TrackSelectHandle > &holder, const std::shared_ptr< Track > &pTrack)
Definition: TrackSelectHandle.cpp:56
TrackSelectHandle::Drag
Result Drag(const TrackPanelMouseEvent &event, AudacityProject *pProject) override
Definition: TrackSelectHandle.cpp:110
UIHandle::Result
unsigned Result
Definition: UIHandle.h:37
UIHandlePtr
std::shared_ptr< UIHandle > UIHandlePtr
Definition: CellularPanel.h:28
HitTestPreview
Definition: HitTestResult.h:20
ProjectHistory::PushState
void PushState(const TranslatableString &desc, const TranslatableString &shortDesc)
Definition: ProjectHistory.cpp:90
TrackList::Get
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:495
TrackList::Leaders
auto Leaders() -> TrackIterRange< TrackType >
Definition: Track.h:1389
TrackSelectHandle::~TrackSelectHandle
virtual ~TrackSelectHandle()
Definition: TrackSelectHandle.cpp:64
ProjectHistory::RollbackState
void RollbackState()
Definition: ProjectHistory.cpp:117
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:113
TrackPanelMouseEvent
Definition: TrackPanelMouseEvent.h:46
TrackPanelMouseState
Definition: TrackPanelMouseEvent.h:28
TrackSelectHandle::mMoveUpThreshold
int mMoveUpThreshold
Definition: TrackSelectHandle.h:58
TrackSelectHandle::Preview
HitTestPreview Preview(const TrackPanelMouseState &state, AudacityProject *pProject) override
Definition: TrackSelectHandle.cpp:145
TrackView::GetChannelGroupHeight
static int GetChannelGroupHeight(const Track *pTrack)
Definition: TrackView.cpp:34
AssignUIHandlePtr
std::shared_ptr< Subclass > AssignUIHandlePtr(std::weak_ptr< Subclass > &holder, const std::shared_ptr< Subclass > &pNew)
Definition: UIHandle.h:145
SelectUtilities::DoListSelection
void DoListSelection(AudacityProject &project, Track *t, bool shift, bool ctrl, bool modifyState)
Definition: SelectUtilities.cpp:112
RefreshCode
Namespace containing an enum 'what to do on a refresh?'.
Definition: RefreshCode.h:16
TrackPanelMouseEvent::event
wxMouseEvent & event
Definition: TrackPanelMouseEvent.h:58
TrackSelectHandle::Cancel
Result Cancel(AudacityProject *) override
Definition: TrackSelectHandle.cpp:207
TrackSelectHandle.h
ProjectHistory::Get
static ProjectHistory & Get(AudacityProject &project)
Definition: ProjectHistory.cpp:26
TrackSelectHandle::CalculateRearrangingThresholds
void CalculateRearrangingThresholds(const wxMouseEvent &event, AudacityProject *project)
Definition: TrackSelectHandle.cpp:217