1 /**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 NoteTrackVZoomHandle.cpp
6
7 Paul Licameli split from TrackPanel.cpp
8
9 **********************************************************************/
10
11
12 #ifdef USE_MIDI
13 #include "NoteTrackVZoomHandle.h"
14
15 #include "../../../ui/TrackVRulerControls.h"
16
17 #include "../../../../HitTestResult.h"
18 #include "../../../../NoteTrack.h"
19 #include "Project.h"
20 #include "../../../../ProjectHistory.h"
21 #include "../../../../RefreshCode.h"
22 #include "../../../../TrackArtist.h"
23 #include "../../../../TrackPanelMouseEvent.h"
24 #include "../../../../widgets/PopupMenuTable.h"
25 #include "../../../../../images/Cursors.h"
26 #include "Prefs.h"
27
28 #include <wx/event.h>
29
30 namespace
31 {
32
33 struct InitMenuData
34 {
35 public:
36 AudacityProject &project;
37 NoteTrack *pTrack;
38 wxRect rect;
39 unsigned result;
40 int yy;
41 };
42
IsDragZooming(int zoomStart,int zoomEnd)43 bool IsDragZooming(int zoomStart, int zoomEnd)
44 {
45 const int DragThreshold = 3;// Anything over 3 pixels is a drag, else a click.
46 bool bVZoom;
47 gPrefs->Read(wxT("/GUI/VerticalZooming"), &bVZoom, false);
48 return bVZoom && (abs(zoomEnd - zoomStart) > DragThreshold);
49 }
50
51 }
52
53 ///////////////////////////////////////////////////////////////////////////////
54
NoteTrackVZoomHandle(const std::shared_ptr<NoteTrack> & pTrack,const wxRect & rect,int y)55 NoteTrackVZoomHandle::NoteTrackVZoomHandle
56 (const std::shared_ptr<NoteTrack> &pTrack, const wxRect &rect, int y)
57 : mpTrack{ pTrack } , mZoomStart(y), mZoomEnd(y), mRect(rect)
58 {
59 }
60
Enter(bool,AudacityProject *)61 void NoteTrackVZoomHandle::Enter(bool, AudacityProject *)
62 {
63 #ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
64 mChangeHighlight = RefreshCode::RefreshCell;
65 #endif
66 }
67
HitPreview(const wxMouseState & state)68 HitTestPreview NoteTrackVZoomHandle::HitPreview(const wxMouseState &state)
69 {
70 static auto zoomInCursor =
71 ::MakeCursor(wxCURSOR_MAGNIFIER, ZoomInCursorXpm, 19, 15);
72 static auto zoomOutCursor =
73 ::MakeCursor(wxCURSOR_MAGNIFIER, ZoomOutCursorXpm, 19, 15);
74 static wxCursor arrowCursor{ wxCURSOR_ARROW };
75
76 bool bVZoom;
77 gPrefs->Read(wxT("/GUI/VerticalZooming"), &bVZoom, false);
78 bVZoom &= !state.RightIsDown();
79 const auto message = bVZoom ?
80 XO("Click to vertically zoom in. Shift-click to zoom out. Drag to specify a zoom region.") :
81 XO("Right-click for menu.");
82
83 return {
84 message,
85 bVZoom ? (state.ShiftDown() ? &*zoomOutCursor : &*zoomInCursor) : &arrowCursor
86 // , message
87 };
88 }
89
HitTest(std::weak_ptr<NoteTrackVZoomHandle> & holder,const wxMouseState & state,const std::shared_ptr<NoteTrack> & pTrack,const wxRect & rect)90 UIHandlePtr NoteTrackVZoomHandle::HitTest
91 (std::weak_ptr<NoteTrackVZoomHandle> &holder,
92 const wxMouseState &state,
93 const std::shared_ptr<NoteTrack> &pTrack, const wxRect &rect)
94 {
95 if (pTrack) {
96 auto result = std::make_shared<NoteTrackVZoomHandle>(
97 pTrack, rect, state.m_y);
98 result = AssignUIHandlePtr(holder, result);
99 return result;
100 }
101 return {};
102 }
103
~NoteTrackVZoomHandle()104 NoteTrackVZoomHandle::~NoteTrackVZoomHandle()
105 {
106 }
107
HandlesRightClick()108 bool NoteTrackVZoomHandle::HandlesRightClick()
109 {
110 return true;
111 }
112
Click(const TrackPanelMouseEvent &,AudacityProject *)113 UIHandle::Result NoteTrackVZoomHandle::Click
114 (const TrackPanelMouseEvent &, AudacityProject *)
115 {
116 // change note track to zoom like audio track
117 // mpTrack->StartVScroll();
118
119 return RefreshCode::RefreshNone;
120 }
121
Drag(const TrackPanelMouseEvent & evt,AudacityProject * pProject)122 UIHandle::Result NoteTrackVZoomHandle::Drag
123 (const TrackPanelMouseEvent &evt, AudacityProject *pProject)
124 {
125 using namespace RefreshCode;
126 auto pTrack = TrackList::Get( *pProject ).Lock(mpTrack);
127 if (!pTrack)
128 return Cancelled;
129
130 const wxMouseEvent &event = evt.event;
131 mZoomEnd = event.m_y;
132 if (IsDragZooming(mZoomStart, mZoomEnd)) {
133 // changed Note track to work like audio track
134 // pTrack->VScroll(mZoomStart, mZoomEnd);
135 return RefreshAll;
136 }
137 return RefreshNone;
138 }
139
Preview(const TrackPanelMouseState & st,AudacityProject *)140 HitTestPreview NoteTrackVZoomHandle::Preview
141 (const TrackPanelMouseState &st, AudacityProject *)
142 {
143 return HitPreview(st.state);
144 }
145
146 enum {
147 OnZoomFitVerticalID = 20000,
148 OnZoomResetID,
149 OnZoomDiv2ID,
150 OnZoomTimes2ID,
151 OnZoomHalfWaveID,
152 OnZoomInVerticalID,
153 OnZoomOutVerticalID,
154
155 OnZoomMaxID,
156
157 OnUpOctaveID,
158 OnDownOctaveID,
159 };
160 ///////////////////////////////////////////////////////////////////////////////
161 // Table class
162
163 class NoteTrackVRulerMenuTable
164 : public PopupMenuTable
165 , private PrefsListener
166 {
NoteTrackVRulerMenuTable()167 NoteTrackVRulerMenuTable()
168 : PopupMenuTable{ "NoteTrackVRuler" }
169 {};
~NoteTrackVRulerMenuTable()170 virtual ~NoteTrackVRulerMenuTable() {}
171 DECLARE_POPUP_MENU(NoteTrackVRulerMenuTable);
172
173 public:
174 static NoteTrackVRulerMenuTable &Instance();
175
176 protected:
177 enum {
178 // Note that these can be with or without spectrum view which
179 // adds a constant.
180 //const int kZoom1to1 = 1;
181 //const int kZoomTimes2 = 2;
182 //const int kZoomDiv2 = 3;
183 //const int kZoomHalfWave = 4;
184 //const int kZoomInByDrag = 5;
185 kZoomIn = 6,
186 kZoomOut = 7,
187 kZoomReset = 8,
188 kZoomMax = 9,
189 kUpOctave = 10,
190 kDownOctave = 11,
191 };
192
193 InitMenuData *mpData {};
194 void OnZoom( int iZoomCode );
195 // void OnZoomFitVertical(wxCommandEvent&){ OnZoom( kZoom1to1 );};
OnZoomReset(wxCommandEvent &)196 void OnZoomReset(wxCommandEvent&){ OnZoom( kZoomReset );};
197 // void OnZoomDiv2Vertical(wxCommandEvent&){ OnZoom( kZoomDiv2 );};
198 // void OnZoomTimes2Vertical(wxCommandEvent&){ OnZoom( kZoomTimes2 );};
199 // void OnZoomHalfWave(wxCommandEvent&){ OnZoom( kZoomHalfWave );};
OnZoomInVertical(wxCommandEvent &)200 void OnZoomInVertical(wxCommandEvent&){ OnZoom( kZoomIn );};
OnZoomOutVertical(wxCommandEvent &)201 void OnZoomOutVertical(wxCommandEvent&){ OnZoom( kZoomOut );};
OnZoomMax(wxCommandEvent &)202 void OnZoomMax(wxCommandEvent&){ OnZoom( kZoomMax );};
OnUpOctave(wxCommandEvent &)203 void OnUpOctave(wxCommandEvent&){ OnZoom( kUpOctave );};
OnDownOctave(wxCommandEvent &)204 void OnDownOctave(wxCommandEvent&){ OnZoom( kDownOctave );};
205
206 private:
207 void InitUserData(void *pUserData) override;
208
UpdatePrefs()209 void UpdatePrefs() override
210 {
211 // Because labels depend on advanced vertical zoom setting
212 PopupMenuTable::Clear();
213 }
214 };
215
Instance()216 NoteTrackVRulerMenuTable &NoteTrackVRulerMenuTable::Instance()
217 {
218 static NoteTrackVRulerMenuTable instance;
219 return instance;
220 }
221
InitUserData(void * pUserData)222 void NoteTrackVRulerMenuTable::InitUserData(void *pUserData)
223 {
224 mpData = static_cast<InitMenuData*>(pUserData);
225 }
226
OnZoom(int iZoomCode)227 void NoteTrackVRulerMenuTable::OnZoom( int iZoomCode ){
228 switch( iZoomCode ){
229 case kZoomReset:
230 mpData->pTrack->ZoomAllNotes();
231 break;
232 case kZoomIn:
233 mpData->pTrack->ZoomIn(mpData->rect, mpData->yy);
234 break;
235 case kZoomOut:
236 mpData->pTrack->ZoomOut(mpData->rect, mpData->yy);
237 break;
238 case kZoomMax:
239 mpData->pTrack->ZoomMaxExtent();
240 break;
241 case kUpOctave:
242 mpData->pTrack->ShiftNoteRange(12);
243 break;
244 case kDownOctave:
245 mpData->pTrack->ShiftNoteRange(-12);
246 break;
247 }
248 AudacityProject *const project = &mpData->project;
249 ProjectHistory::Get( *project ).ModifyState(false);
250 using namespace RefreshCode;
251 mpData->result = UpdateVRuler | RefreshAll;
252 }
253
254
255 BEGIN_POPUP_MENU(NoteTrackVRulerMenuTable)
256
257 // Accelerators only if zooming enabled.
258 bool bVZoom;
259 gPrefs->Read(wxT("/GUI/VerticalZooming"), &bVZoom, false);
260
261 BeginSection( "Zoom" );
262 BeginSection( "Basic" );
263 AppendItem( "Reset", OnZoomResetID,
264 MakeLabel( XXO("Zoom Reset"), bVZoom, XXO("Shift-Right-Click")),
265 POPUP_MENU_FN( OnZoomReset ) );
266 AppendItem( "Max", OnZoomMaxID, XXO("Max Zoom"), POPUP_MENU_FN( OnZoomMax ) );
267 EndSection();
268
269 BeginSection( "InOut" );
270 AppendItem( "In", OnZoomInVerticalID,
271 MakeLabel( XXO("Zoom In"), bVZoom, XXO("Left-Click/Left-Drag") ),
272 POPUP_MENU_FN( OnZoomInVertical ) );
273 AppendItem( "Out", OnZoomOutVerticalID,
274 MakeLabel( XXO("Zoom Out"), bVZoom, XXO("Shift-Left-Click") ),
275 POPUP_MENU_FN( OnZoomOutVertical ) );
276 EndSection();
277 EndSection();
278
279 BeginSection( "Pan" );
280 BeginSection( "Octaves" );
281 AppendItem( "Up", OnUpOctaveID, XXO("Up &Octave"), POPUP_MENU_FN( OnUpOctave) );
282 AppendItem( "Down", OnDownOctaveID, XXO("Down Octa&ve"), POPUP_MENU_FN( OnDownOctave ) );
283 EndSection();
284 EndSection();
285
END_POPUP_MENU()286 END_POPUP_MENU()
287
288
289
290 UIHandle::Result NoteTrackVZoomHandle::Release
291 (const TrackPanelMouseEvent &evt, AudacityProject *pProject,
292 wxWindow *pParent)
293 {
294 using namespace RefreshCode;
295 auto pTrack = TrackList::Get( *pProject ).Lock(mpTrack);
296 if (!pTrack)
297 return RefreshNone;
298
299 const wxMouseEvent &event = evt.event;
300 //const bool shiftDown = event.ShiftDown();
301 const bool rightUp = event.RightUp();
302
303
304 // Popup menu...
305 if (
306 rightUp &&
307 !(event.ShiftDown() || event.CmdDown()))
308 {
309 InitMenuData data {
310 *pProject, pTrack.get(), mRect, RefreshNone, event.m_y
311 };
312
313 PopupMenuTable *const pTable =
314 (PopupMenuTable *) &NoteTrackVRulerMenuTable::Instance();
315 auto pMenu = PopupMenuTable::BuildMenu(pTable, &data);
316
317 pMenu->Popup( *pParent, { event.m_x, event.m_y } );
318
319 return data.result;
320 }
321
322 bool bVZoom;
323 gPrefs->Read(wxT("/GUI/VerticalZooming"), &bVZoom, false);
324 bVZoom &= event.GetId() != kCaptureLostEventId;
325 if( !bVZoom )
326 return RefreshAll;
327
328 if (IsDragZooming(mZoomStart, mZoomEnd)) {
329 pTrack->ZoomTo(evt.rect, mZoomStart, mZoomEnd);
330 }
331 else if (event.ShiftDown() || event.RightUp()) {
332 if (event.ShiftDown() && event.RightUp()) {
333 auto oldBotNote = pTrack->GetBottomNote();
334 auto oldTopNote = pTrack->GetTopNote();
335 // Zoom out to show all notes
336 pTrack->ZoomAllNotes();
337 if (pTrack->GetBottomNote() == oldBotNote &&
338 pTrack->GetTopNote() == oldTopNote) {
339 // However if we are already showing all notes, zoom out further
340 pTrack->ZoomMaxExtent();
341 }
342 } else {
343 // Zoom out
344 pTrack->ZoomOut(evt.rect, mZoomEnd);
345 }
346 }
347 else {
348 pTrack->ZoomIn(evt.rect, mZoomEnd);
349 }
350
351 mZoomEnd = mZoomStart = 0;
352 ProjectHistory::Get( *pProject ).ModifyState(false);
353
354 return RefreshAll;
355 }
356
Cancel(AudacityProject * WXUNUSED (pProject))357 UIHandle::Result NoteTrackVZoomHandle::Cancel(AudacityProject *WXUNUSED(pProject))
358 {
359 // Cancel is implemented! And there is no initial state to restore,
360 // so just return a code.
361 return RefreshCode::RefreshAll;
362 }
363
Draw(TrackPanelDrawingContext & context,const wxRect & rect,unsigned iPass)364 void NoteTrackVZoomHandle::Draw(
365 TrackPanelDrawingContext &context,
366 const wxRect &rect, unsigned iPass )
367 {
368 if ( iPass == TrackArtist::PassZooming ) {
369 if (!mpTrack.lock()) //? TrackList::Lock()
370 return;
371
372 if ( IsDragZooming( mZoomStart, mZoomEnd ) )
373 TrackVRulerControls::DrawZooming
374 ( context, rect, mZoomStart, mZoomEnd );
375 }
376 }
377
DrawingArea(TrackPanelDrawingContext &,const wxRect & rect,const wxRect & panelRect,unsigned iPass)378 wxRect NoteTrackVZoomHandle::DrawingArea(
379 TrackPanelDrawingContext &,
380 const wxRect &rect, const wxRect &panelRect, unsigned iPass )
381 {
382 if ( iPass == TrackArtist::PassZooming )
383 return TrackVRulerControls::ZoomingArea( rect, panelRect );
384 else
385 return rect;
386 }
387
388 #endif
389