Audacity  2.2.2
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 #include "../../Audacity.h"
12 #include "TrackSelectHandle.h"
13 #include "TrackControls.h"
14 #include "../../Experimental.h"
15 #include "../../HitTestResult.h"
16 #include "../../MixerBoard.h"
17 #include "../../Project.h"
18 #include "../../RefreshCode.h"
19 #include "../../TrackPanel.h"
20 #include "../../TrackPanelMouseEvent.h"
21 #include "../../WaveTrack.h"
22 
23 #include "../../MemoryX.h"
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 _("Command-Click")
32 #else
33 /* i18n-hint: Ctrl names a modifier key on Windows or Linux keyboards */
34 #define CTRL_CLICK _("Ctrl-Click")
35 #endif
36 
37 namespace {
38  wxString Message(unsigned trackCount) {
39  if (trackCount > 1)
40  // i18n-hint: %s is replaced by (translation of) 'Ctrl-Click' on windows, 'Command-Click' on Mac
41  return wxString::Format(
42  _("%s to select or deselect track. Drag up or down to change track order."),
43  CTRL_CLICK );
44  else
45  // i18n-hint: %s is replaced by (translation of) 'Ctrl-Click' on windows, 'Command-Click' on Mac
46  return wxString::Format(
47  _("%s to select or deselect track."),
48  CTRL_CLICK );
49  }
50 }
51 
52 TrackSelectHandle::TrackSelectHandle( const std::shared_ptr<Track> &pTrack )
53  : mpTrack( pTrack )
54 {}
55 
57 (std::weak_ptr<TrackSelectHandle> &holder,
58  const std::shared_ptr<Track> &pTrack)
59 {
60  auto result = std::make_shared<TrackSelectHandle>(pTrack);
61  result = AssignUIHandlePtr(holder, result);
62  return result;
63 }
64 
66 {
67 }
68 
70 (const TrackPanelMouseEvent &evt, AudacityProject *pProject)
71 {
72  // If unsafe to drag, still, it does harmlessly change the selected track
73  // set on button down.
74 
75  using namespace RefreshCode;
76  Result result = RefreshNone;
77 
78  const wxMouseEvent &event = evt.event;
79 
80  // AS: If not a click, ignore the mouse event.
81  if (!event.ButtonDown() && !event.ButtonDClick())
82  return Cancelled;
83  if (!event.Button(wxMOUSE_BTN_LEFT))
84  return Cancelled;
85 
86  const auto pTrack = mpTrack;
87  if (!pTrack)
88  return Cancelled;
89  const bool unsafe = pProject->IsAudioActive();
90 
91  // DM: If they weren't clicking on a particular part of a track label,
92  // deselect other tracks and select this one.
93 
94  // JH: also, capture the current track for rearranging, so the user
95  // can drag the track up or down to swap it with others
96  if (unsafe)
97  result |= Cancelled;
98  else {
99  mRearrangeCount = 0;
100  CalculateRearrangingThresholds(event);
101  }
102 
103  pProject->HandleListSelection
104  (pTrack.get(), event.ShiftDown(), event.ControlDown(), !unsafe);
105 
106  mClicked = true;
107  return result;
108 }
109 
111 (const TrackPanelMouseEvent &evt, AudacityProject *pProject)
112 {
113  using namespace RefreshCode;
114  Result result = RefreshNone;
115 
116  const wxMouseEvent &event = evt.event;
117 
118  TrackList *const tracks = pProject->GetTracks();
119 
120  // probably harmless during play? However, we do disallow the click, so check this too.
121  bool unsafe = pProject->IsAudioActive();
122  if (unsafe)
123  return result;
124 
125  MixerBoard* pMixerBoard = pProject->GetMixerBoard(); // Update mixer board, too.
126  if (event.m_y < mMoveUpThreshold || event.m_y < 0) {
127  tracks->MoveUp(mpTrack.get());
128  --mRearrangeCount;
129  if (pMixerBoard)
130  if(auto pPlayable = dynamic_cast< const PlayableTrack* >( mpTrack.get() ))
131  pMixerBoard->MoveTrackCluster(pPlayable, true /* up */);
132  }
133  else if ( event.m_y > mMoveDownThreshold
134  || event.m_y > evt.whole.GetHeight() ) {
135  tracks->MoveDown(mpTrack.get());
136  ++mRearrangeCount;
137  if (pMixerBoard)
138  if(auto pPlayable = dynamic_cast< const PlayableTrack* >( mpTrack.get() ))
139  pMixerBoard->MoveTrackCluster(pPlayable, false /* down */);
140  }
141  else
142  return result;
143 
144  // JH: if we moved up or down, recalculate the thresholds and make sure the
145  // track is fully on-screen.
146  CalculateRearrangingThresholds(event);
147 
148  result |= EnsureVisible | RefreshAll;
149  return result;
150 }
151 
153 (const TrackPanelMouseState &, const AudacityProject *project)
154 {
155  const auto trackCount = project->GetTrackPanel()->GetTrackCount();
156  auto message = Message(trackCount);
157  if (mClicked) {
158  static auto disabledCursor =
159  ::MakeCursor(wxCURSOR_NO_ENTRY, DisabledCursorXpm, 16, 16);
160  static wxCursor rearrangeCursor{ wxCURSOR_HAND };
161 
162  const bool unsafe = GetActiveProject()->IsAudioActive();
163  return {
164  message,
165  (unsafe
166  ? &*disabledCursor
167  : &rearrangeCursor)
168  // , message // Stop showing the tooltip after the click
169  };
170  }
171  else {
172  // Only mouse-over
173  // Don't test safety, because the click to change selection is allowed
174  static wxCursor arrowCursor{ wxCURSOR_ARROW };
175  return {
176  message,
177  &arrowCursor,
178  message
179  };
180  }
181 }
182 
184 (const TrackPanelMouseEvent &, AudacityProject *, wxWindow *)
185 {
186  // If we're releasing, surely we are dragging a track?
187  wxASSERT( mpTrack );
188  if (mRearrangeCount != 0) {
189  AudacityProject *const project = ::GetActiveProject();
190  project->PushState(
191  wxString::Format(
192  /* i18n-hint: will substitute name of track for %s */
193  ( mRearrangeCount < 0 ? _("Moved '%s' up") : _("Moved '%s' down") ),
194  mpTrack->GetName()
195  ),
196  _("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  pProject->RollbackState();
210  // Bug 1677
211  mpTrack.reset();
213 }
214 
218 {
219  // JH: this will probably need to be tweaked a bit, I'm just
220  // not sure what formula will have the best feel for the
221  // user.
222 
223  AudacityProject *const project = ::GetActiveProject();
224  TrackList *const tracks = project->GetTracks();
225 
226  if (tracks->CanMoveUp(mpTrack.get()))
228  event.m_y - tracks->GetGroupHeight(tracks->GetPrev(mpTrack.get(), true));
229  else
230  mMoveUpThreshold = INT_MIN;
231 
232  if (tracks->CanMoveDown(mpTrack.get()))
234  event.m_y + tracks->GetGroupHeight(tracks->GetNext(mpTrack.get(), true));
235  else
236  mMoveDownThreshold = INT_MAX;
237 }
size_t GetTrackCount() const
A list of TrackListNode items.
Definition: Track.h:611
bool CanMoveDown(Track *t) const
Definition: Track.cpp:1116
Result Cancel(AudacityProject *) override
std::shared_ptr< Track > mpTrack
Result Release(const TrackPanelMouseEvent &event, AudacityProject *pProject, wxWindow *pParent) override
MixerBoard * GetMixerBoard()
Definition: Project.h:500
std::unique_ptr< wxCursor > MakeCursor(int WXUNUSED(CursorId), const char *pXpm[36], int HotX, int HotY)
Definition: TrackPanel.cpp:274
Result Drag(const TrackPanelMouseEvent &event, AudacityProject *pProject) override
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:158
unsigned Result
Definition: UIHandle.h:37
bool CanMoveUp(Track *t) const
Definition: Track.cpp:1111
Track * GetNext(Track *t, bool linked=false) const
Return a track in the list that comes after Track t.
Definition: Track.cpp:1043
void RollbackState()
Definition: Project.cpp:4542
void CalculateRearrangingThresholds(const wxMouseEvent &event)
void MoveTrackCluster(const PlayableTrack *pTrack, bool bUp)
bool IsAudioActive() const
Definition: Project.cpp:1423
std::shared_ptr< UIHandle > UIHandlePtr
Definition: TrackPanel.h:59
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom"))), OnMoveTrack) void TrackMenuTable::OnSetName(wxCommandEvent &)
int GetGroupHeight(Track *t) const
Definition: Track.cpp:1100
Track * GetPrev(Track *t, bool linked=false) const
Definition: Track.cpp:1062
void PushState(const wxString &desc, const wxString &shortDesc)
Definition: Project.cpp:4502
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:300
#define CTRL_CLICK
TrackPanel * GetTrackPanel()
Definition: Project.h:289
Result Click(const TrackPanelMouseEvent &event, AudacityProject *pProject) override
static UIHandlePtr HitAnywhere(std::weak_ptr< TrackSelectHandle > &holder, const std::shared_ptr< Track > &pTrack)
HitTestPreview Preview(const TrackPanelMouseState &state, const AudacityProject *pProject) override
TrackList * GetTracks()
Definition: Project.h:174
TrackSelectHandle(const TrackSelectHandle &)=delete
std::shared_ptr< Subclass > AssignUIHandlePtr(std::weak_ptr< Subclass > &holder, const std::shared_ptr< Subclass > &pNew)
Definition: UIHandle.h:162
void HandleListSelection(Track *t, bool shift, bool ctrl, bool modifyState)
Definition: Menus.cpp:3419