Audacity 3.2.0
TrackSelectHandle.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5TrackSelectHandle.cpp
6
7Paul Licameli split from TrackPanel.cpp
8
9**********************************************************************/
10
11
12#include "TrackSelectHandle.h"
13
14#include "ChannelView.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
37namespace {
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
51TrackSelectHandle::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
68std::shared_ptr<const Track> TrackSelectHandle::FindTrack() const
69{
70 return mpTrack;
71}
72
74(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
75{
76 // If unsafe to drag, still, it does harmlessly change the selected track
77 // set on button down.
78
79 using namespace RefreshCode;
80 Result result = RefreshNone;
81
82 const wxMouseEvent &event = evt.event;
83
84 // AS: If not a click, ignore the mouse event.
85 if (!event.ButtonDown() && !event.ButtonDClick())
86 return Cancelled;
87 if (!event.Button(wxMOUSE_BTN_LEFT))
88 return Cancelled;
89
90 const auto pTrack = mpTrack;
91 if (!pTrack)
92 return Cancelled;
93 const bool unsafe = ProjectAudioIO::Get( *pProject ).IsAudioActive();
94
95 // DM: If they weren't clicking on a particular part of a track label,
96 // deselect other tracks and select this one.
97
98 // JH: also, capture the current track for rearranging, so the user
99 // can drag the track up or down to swap it with others
100 if (unsafe)
101 result |= Cancelled;
102 else {
103 mRearrangeCount = 0;
104 CalculateRearrangingThresholds(event, pProject);
105 }
106
108 *pTrack, event.ShiftDown(), event.ControlDown(), !unsafe);
109
110 mClicked = true;
111 return result;
112}
113
115(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
116{
117 using namespace RefreshCode;
118 Result result = RefreshNone;
119 if (!mpTrack)
120 return result;
121
122 const wxMouseEvent &event = evt.event;
123
124 auto &tracks = TrackList::Get( *pProject );
125
126 // probably harmless during play? However, we do disallow the click, so check this too.
127 bool unsafe = ProjectAudioIO::Get( *pProject ).IsAudioActive();
128 if (unsafe)
129 return result;
130
131 if (event.m_y < mMoveUpThreshold || event.m_y < 0) {
132 tracks.MoveUp(*mpTrack);
134 }
135 else if ( event.m_y > mMoveDownThreshold
136 || event.m_y > evt.whole.GetHeight() ) {
137 tracks.MoveDown(*mpTrack);
139 }
140 else
141 return result;
142
143 // JH: if we moved up or down, recalculate the thresholds and make sure the
144 // track is fully on-screen.
145 CalculateRearrangingThresholds(event, pProject);
146
147 result |= EnsureVisible | RefreshAll;
148 return result;
149}
150
153{
154 static auto disabledCursor =
155 ::MakeCursor(wxCURSOR_NO_ENTRY, DisabledCursorXpm, 16, 16);
156 //static wxCursor rearrangeCursor{ wxCURSOR_HAND };
157 static auto rearrangingCursor =
158 ::MakeCursor(wxCURSOR_HAND, RearrangingCursorXpm, 16, 16);
159 static wxCursor arrowCursor{ wxCURSOR_ARROW };
160
161 //static auto hoverCursor =
162 // ::MakeCursor(wxCURSOR_HAND, RearrangeCursorXpm, 16, 16);
163 //static auto clickedCursor =
164 // ::MakeCursor(wxCURSOR_HAND, RearrangingCursorXpm, 16, 16);
165
166 const auto trackCount = TrackList::Get( *project ).Any().size();
167 auto message = Message(trackCount);
168 if (mClicked) {
169 const bool unsafe =
171 const bool canMove = TrackList::Get( *project ).Any().size() > 1;
172 return {
173 message,
174 (unsafe
175 ? &*disabledCursor
176 : canMove
177 ? &*rearrangingCursor
178 : &arrowCursor)
179 // , message // Stop showing the tooltip after the click
180 };
181 }
182 else {
183 // Only mouse-over
184 // Don't test safety, because the click to change selection is allowed
185 return {
186 message,
187 &arrowCursor,
188 message
189 };
190 }
191}
192
194(const TrackPanelMouseEvent &, AudacityProject *project, wxWindow *)
195{
196 // If we're releasing, surely we are dragging a track?
197 wxASSERT( mpTrack );
198 if (mRearrangeCount != 0) {
199 ProjectHistory::Get( *project ).PushState(
200 /* i18n-hint: will substitute name of track for %s */
201 ( mRearrangeCount < 0 ? XO("Moved '%s' up") : XO("Moved '%s' down") )
202 .Format( mpTrack->GetName() ),
203 XO("Move Track"));
204 }
205 // Bug 1677
206 // Holding on to the reference to the track was causing it to be released far later
207 // than necessary, on shutdown, and so causing a crash as a dialog about cleaning
208 // out files could not show at that time.
209 mpTrack.reset();
210 // No need to redraw, that was done when drag moved the track
212}
213
215{
216 ProjectHistory::Get( *pProject ).RollbackState();
217 // Bug 1677
218 mpTrack.reset();
220}
221
225 const wxMouseEvent & event, AudacityProject *project)
226{
227 // JH: this will probably need to be tweaked a bit, I'm just
228 // not sure what formula will have the best feel for the
229 // user.
230
231 auto &tracks = TrackList::Get( *project );
232
233 if (mpTrack && tracks.CanMoveUp(*mpTrack))
235 event.m_y -
237 * -- tracks.Find(mpTrack.get()));
238 else
239 mMoveUpThreshold = INT_MIN;
240
241 if (mpTrack && tracks.CanMoveDown(*mpTrack))
243 event.m_y +
245 * ++ tracks.Find(mpTrack.get()));
246 else
247 mMoveDownThreshold = INT_MAX;
248}
std::shared_ptr< UIHandle > UIHandlePtr
Definition: CellularPanel.h:28
XO("Cut/Copy/Paste")
const auto tracks
const auto project
declares abstract base class Track, TrackList, and iterators over TrackList
std::unique_ptr< wxCursor > MakeCursor(int WXUNUSED(CursorId), const char *const pXpm[36], int HotX, int HotY)
Definition: TrackPanel.cpp:189
#define CTRL_CLICK
std::shared_ptr< Subclass > AssignUIHandlePtr(std::weak_ptr< Subclass > &holder, const std::shared_ptr< Subclass > &pNew)
Definition: UIHandle.h:164
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
static int GetChannelGroupHeight(const Track *pTrack)
Definition: ChannelView.cpp:39
Abstract base class used in importing a file.
bool IsAudioActive() const
static ProjectAudioIO & Get(AudacityProject &project)
void PushState(const TranslatableString &desc, const TranslatableString &shortDesc)
static ProjectHistory & Get(AudacityProject &project)
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:950
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:314
std::shared_ptr< const Track > FindTrack() const override
static UIHandlePtr HitAnywhere(std::weak_ptr< TrackSelectHandle > &holder, const std::shared_ptr< Track > &pTrack)
std::shared_ptr< Track > mpTrack
Result Release(const TrackPanelMouseEvent &event, AudacityProject *pProject, wxWindow *pParent) override
void CalculateRearrangingThresholds(const wxMouseEvent &event, AudacityProject *project)
Result Drag(const TrackPanelMouseEvent &event, AudacityProject *pProject) override
HitTestPreview Preview(const TrackPanelMouseState &state, AudacityProject *pProject) override
Result Click(const TrackPanelMouseEvent &event, AudacityProject *pProject) override
Result Cancel(AudacityProject *) override
TrackSelectHandle(const TrackSelectHandle &)=delete
Holds a msgid for the translation catalog; may also bind format arguments.
unsigned Result
Definition: UIHandle.h:40
Namespace containing an enum 'what to do on a refresh?'.
Definition: RefreshCode.h:16
void DoListSelection(AudacityProject &project, Track &t, bool shift, bool ctrl, bool modifyState)
TranslatableString Message(unsigned trackCount)