Audacity 3.2.0
NoteTrackControls.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5NoteTrackControls.cpp
6
7Paul Licameli split from TrackPanel.cpp
8
9**********************************************************************/
10
11
12#ifdef USE_MIDI
13#include "NoteTrackControls.h"
15#include "AColor.h"
16#include "AllThemeResources.h"
17#include "../../ui/PlayableTrackButtonHandles.h"
20#include "../../../../TrackArtist.h"
21#include "../../../../TrackPanel.h"
22#include "../../../ui/CommonTrackInfo.h"
23#include "../../../../TrackPanelMouseEvent.h"
24#include "NoteTrack.h"
25#include "../../../../widgets/PopupMenuTable.h"
26#include "ProjectHistory.h"
27#include "../../../../ProjectWindows.h"
28#include "../../../../RefreshCode.h"
29#include "Theme.h"
30#include <wx/frame.h>
31
34{
35}
36
37std::vector<UIHandlePtr> NoteTrackControls::HitTest
38(const TrackPanelMouseState & st,
39 const AudacityProject *pProject)
40{
41 // Hits are mutually exclusive, results single
42 std::vector<UIHandlePtr> results;
43 const wxMouseState &state = st.state;
44 const wxRect &rect = st.rect;
45 if (state.ButtonIsDown(wxMOUSE_BTN_ANY)) {
46 auto track = std::static_pointer_cast<NoteTrack>(FindTrack());
47 auto result = [&]{
48 UIHandlePtr result;
49 if (NULL != (result = MuteButtonHandle::HitTest(
50 mMuteHandle, state, rect, pProject, track)))
51 return result;
52
53 if (NULL != (result = SoloButtonHandle::HitTest(
54 mSoloHandle, state, rect, pProject, track)))
55 return result;
56 if (NULL != (result = VelocitySliderHandle::HitTest(
57 mVelocityHandle, state, rect, track)))
58 return result;
59 if (NULL != (result = NoteTrackButtonHandle::HitTest(
60 mClickHandle, state, rect, track)))
61 return result;
62 return result;
63 }();
64 if (result) {
65 results.push_back(result);
66 return results;
67 }
68 }
69
70 return PlayableTrackControls::HitTest(st, pProject);
71}
72
74{
76 : PopupMenuTable{ "NoteTrack" }
77 {}
79
80public:
82
83private:
84 void InitUserData(void *pUserData) override
85 {
86 mpData = static_cast<PlayableTrackControls::InitMenuData*>(pUserData);
87 }
88
90
91 void OnChangeOctave(wxCommandEvent &);
92};
93
95{
96 static NoteTrackMenuTable instance;
97 return instance;
98}
99
100enum {
103};
104
106void NoteTrackMenuTable::OnChangeOctave(wxCommandEvent &event)
107{
108 auto &track = static_cast<NoteTrack&>(mpData->track);
109
110 wxASSERT(event.GetId() == OnUpOctaveID
111 || event.GetId() == OnDownOctaveID);
112
113 const bool bDown = (OnDownOctaveID == event.GetId());
114 NoteTrackRange::Get(track).ShiftNoteRange((bDown) ? -12 : 12);
115
118 .ModifyState(false);
120}
121
123 BeginSection( "Basic" );
124 AppendItem( "Up", OnUpOctaveID, XXO("Up &Octave"), POPUP_MENU_FN( OnChangeOctave ) );
125 AppendItem( "Down", OnDownOctaveID, XXO("Down Octa&ve"), POPUP_MENU_FN( OnChangeOctave ) );
128
130{
131#if defined(USE_MIDI)
133#else
134 return NULL;
135#endif
136}
137
138// drawing related
139#include "../../../../widgets/ASlider.h"
140#include "../../../../TrackPanelDrawingContext.h"
141#include "ViewInfo.h"
142
144
145#ifdef USE_MIDI
146enum : int {
147 // PRL: was it correct to include the margin?
151#endif
152
155
156namespace {
157void GetMidiControlsHorizontalBounds( const wxRect &rect, wxRect &dest )
158{
159 dest.x = rect.x;
160 dest.width = rect.width / 4 * 4;
161}
162
164( LWSlider *(*Selector)
165 (const wxRect &sliderRect, const NoteTrack *t, bool captured, wxWindow*),
166 wxDC *dc, const wxRect &rect, const Track *pTrack,
167 wxWindow *pParent,
168 bool captured, bool highlight )
169{
170 wxRect sliderRect = rect;
172 auto nt = static_cast<const NoteTrack*>( pTrack );
173 Selector( sliderRect, nt, captured, pParent )->OnPaint(*dc, highlight);
174}
175
177( TrackPanelDrawingContext &context,
178 const wxRect &rect, const Track *pTrack )
179{
180 auto dc = &context.dc;
181 auto target = dynamic_cast<VelocitySliderHandle*>( context.target.get() );
182 bool hit = target && target->GetTrack().get() == pTrack;
183 bool captured = hit && target->IsDragging();
184
185 const auto artist = TrackArtist::Get( context );
186 auto pParent = FindProjectFrame( artist->parent->GetProject() );
187
189 &NoteTrackControls::VelocitySlider, dc, rect, pTrack,
190 pParent, captured, hit);
191}
192
193// Draws the midi channel toggle buttons within the given rect.
194// The rect should be evenly divisible by 4 on both axes.
196( const NoteTrack *pTrack, wxDC & dc, const wxRect &rect, int highlightedChannel )
197{
198 dc.SetTextForeground(theTheme.Colour(clrLabelTrackText));
199 wxASSERT_MSG(rect.width % 4 == 0, "Midi channel control rect width must be divisible by 4");
200 wxASSERT_MSG(rect.height % 4 == 0, "Midi channel control rect height must be divisible by 4");
201
202 auto cellWidth = rect.width / 4;
203 auto cellHeight = rect.height / 4;
204
205 wxRect box;
206 for (int row = 0; row < 4; row++) {
207 for (int col = 0; col < 4; col++) {
208 // chanName is the "external" channel number (1-16)
209 // used by AColor and button labels
210 int chanName = row * 4 + col + 1;
211
212 box.x = rect.x + col * cellWidth;
213 box.y = rect.y + row * cellHeight;
214 box.width = cellWidth;
215 box.height = cellHeight;
216
217 bool visible = pTrack ? pTrack->IsVisibleChan(chanName - 1) : true;
218 if (visible) {
219 // highlightedChannel counts 0 based
220 if ( chanName == highlightedChannel + 1 )
221 AColor::LightMIDIChannel(&dc, chanName);
222 else
223 AColor::MIDIChannel(&dc, chanName);
224 dc.DrawRectangle(box);
225// two choices: channel is enabled (to see and play) when button is in
226// "up" position (original Audacity style) or in "down" position
227//
228#define CHANNEL_ON_IS_DOWN 1
229#if CHANNEL_ON_IS_DOWN
230 AColor::DarkMIDIChannel(&dc, chanName);
231#else
232 AColor::LightMIDIChannel(&dc, chanName);
233#endif
234 AColor::Line(dc, box.x, box.y, box.x + box.width - 1, box.y);
235 AColor::Line(dc, box.x, box.y, box.x, box.y + box.height - 1);
236
237#if CHANNEL_ON_IS_DOWN
238 AColor::LightMIDIChannel(&dc, chanName);
239#else
240 AColor::DarkMIDIChannel(&dc, chanName);
241#endif
242 AColor::Line(dc,
243 box.x + box.width - 1, box.y,
244 box.x + box.width - 1, box.y + box.height - 1);
245 AColor::Line(dc,
246 box.x, box.y + box.height - 1,
247 box.x + box.width - 1, box.y + box.height - 1);
248 } else {
249 if ( chanName == highlightedChannel + 1 )
250 AColor::LightMIDIChannel(&dc, chanName);
251 else
252 AColor::MIDIChannel(&dc, 0);
253 dc.DrawRectangle(box);
254#if CHANNEL_ON_IS_DOWN
256#else
258#endif
259 AColor::Line(dc, box.x, box.y, box.x + box.width - 1, box.y);
260 AColor::Line(dc, box.x, box.y, box.x, box.y + box.height - 1);
261
262#if CHANNEL_ON_IS_DOWN
264#else
266#endif
267 AColor::Line(dc,
268 box.x + box.width - 1, box.y,
269 box.x + box.width - 1, box.y + box.height - 1);
270 AColor::Line(dc,
271 box.x, box.y + box.height - 1,
272 box.x + box.width - 1, box.y + box.height - 1);
273
274 }
275
276 wxString text;
277 wxCoord w;
278 wxCoord h;
279
280 text.Printf(wxT("%d"), chanName);
281 dc.GetTextExtent(text, &w, &h);
282
283 dc.DrawText(text, box.x + (box.width - w) / 2, box.y + (box.height - h) / 2);
284 }
285 }
286 dc.SetTextForeground(theTheme.Colour(clrTrackPanelText));
287 AColor::MIDIChannel(&dc, 0); // always return with gray color selected
288}
289
291( TrackPanelDrawingContext &context,
292 const wxRect &rect, const Track *pTrack )
293{
294 auto target = dynamic_cast<NoteTrackButtonHandle*>( context.target.get() );
295 bool hit = target && target->GetTrack().get() == pTrack;
296 auto channel = hit ? target->GetChannel() : -1;
297 auto &dc = context.dc;
298 wxRect midiRect = rect;
299 GetMidiControlsHorizontalBounds(rect, midiRect);
301 ( static_cast<const NoteTrack *>(pTrack), dc, midiRect, channel );
302}
303}
304
305static const struct NoteTrackTCPLines
307 *static_cast<TCPLines*>(this) =
309 insert( end(), {
314 } );
316
317void NoteTrackControls::GetVelocityRect(const wxRect &rect_, wxRect & dest)
318{
319 const auto rect = wxRect(rect_).Deflate(CommonTrackInfo::Margin);
321 const auto results = CalcItemY( noteTrackTCPLines, TCPLine::kItemVelocity );
322 dest.y = rect.y + results.first;
323 dest.height = results.second;
324}
325
326void NoteTrackControls::GetMidiControlsRect(const wxRect & rect_, wxRect & dest)
327{
328 const auto rect = wxRect(rect_).Deflate(CommonTrackInfo::Margin);
330 const auto results = TrackInfo::CalcItemY(
332 dest.y = rect.y + results.first;
333 dest.height = results.second;
334}
335
337{
339}
340
342{
343 return noteTrackTCPLines;
344};
345
346namespace {
347
348 std::unique_ptr<LWSlider>
351 ;
352
353}
354
356(const wxRect &sliderRect, const NoteTrack *t, bool captured, wxWindow *pParent)
357{
358 static std::once_flag flag;
359 std::call_once( flag, []{ ReCreateVelocitySlider({}); });
361
362 wxPoint pos = sliderRect.GetPosition();
363 float velocity = t ? t->GetVelocity() : 0.0;
364
365 gVelocity->Move(pos);
366 gVelocity->Set(velocity);
367 gVelocityCaptured->Move(pos);
368 gVelocityCaptured->Set(velocity);
369
370 auto slider = (captured ? gVelocityCaptured : gVelocity).get();
371 slider->SetParent( pParent );
372 return slider;
373}
374
376{
377 if (message.appearance)
378 return;
379
380 const auto sliderRect = wxRect(0, 0, kTrackInfoSliderWidth, kTrackInfoSliderHeight);
381
382 /* i18n-hint: Title of the Velocity slider, used to adjust the volume of note tracks */
383 gVelocity = std::make_unique<LWSlider>(nullptr, XO("Velocity"),
384 wxPoint(sliderRect.x, sliderRect.y),
385 wxSize(sliderRect.width, sliderRect.height),
386 VEL_SLIDER);
387 gVelocity->SetDefaultValue(0.0);
388 gVelocityCaptured = std::make_unique<LWSlider>(nullptr, XO("Velocity"),
389 wxPoint(sliderRect.x, sliderRect.y),
390 wxSize(sliderRect.width, sliderRect.height),
391 VEL_SLIDER);
392 gVelocityCaptured->SetDefaultValue(0.0);
393}
394
397 return [](NoteTrack &track) {
398 return std::make_shared<NoteTrackControls>( track.SharedPointer() );
399 };
400}
401
402#include "../../../ui/ChannelView.h"
403
406 return [](NoteTrack &) {
408 };
409}
410
411#endif
#define VEL_SLIDER
Definition: ASlider.h:37
wxT("CloseDown"))
std::shared_ptr< UIHandle > UIHandlePtr
Definition: CellularPanel.h:28
std::vector< TrackInfo::TCPLine > TCPLines
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
DEFINE_ATTACHED_VIRTUAL_OVERRIDE(DoGetNoteTrackControls)
EndSection()
NoteTrackTCPLines noteTrackTCPLines
TrackInfo::TCPLine TCPLine
BeginSection("Basic")
@ kMidiCellWidth
@ kMidiCellHeight
@ OnUpOctaveID
@ OnDownOctaveID
AppendItem("Up", OnUpOctaveID, XXO("Up &Octave"), POPUP_MENU_FN(OnChangeOctave))
#define END_POPUP_MENU()
#define BEGIN_POPUP_MENU(HandlerClass)
#define POPUP_MENU_FN(memFn)
wxFrame * FindProjectFrame(AudacityProject *project)
Get a pointer to the window associated with a project, or null if the given pointer is null,...
const auto project
THEME_API Theme theTheme
Definition: Theme.cpp:82
@ kTrackInfoSliderWidth
Definition: ViewInfo.h:100
@ kTrackInfoSliderExtra
Definition: ViewInfo.h:102
@ kTrackInfoBtnSize
Definition: ViewInfo.h:98
@ kTrackInfoSliderHeight
Definition: ViewInfo.h:99
static std::once_flag flag
@ kTrackInfoWidth
Definition: ZoomInfo.h:30
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:194
static void LightMIDIChannel(wxDC *dc, int channel)
Definition: AColor.cpp:654
static void MIDIChannel(wxDC *dc, int channel)
Definition: AColor.cpp:638
static void DarkMIDIChannel(wxDC *dc, int channel)
Definition: AColor.cpp:672
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
std::shared_ptr< ChannelType > GetChannel(size_t iChannel)
Retrieve a channel, cast to the given type.
Definition: Channel.h:320
virtual std::vector< UIHandlePtr > HitTest(const TrackPanelMouseState &state, const AudacityProject *) override=0
std::shared_ptr< Track > FindTrack()
Lightweight version of ASlider. In other words it does not have a window permanently associated with ...
Definition: ASlider.h:64
static UIHandlePtr HitTest(std::weak_ptr< MuteButtonHandle > &holder, const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr< Track > &pTrack)
std::shared_ptr< NoteTrack > GetTrack() const
static UIHandlePtr HitTest(std::weak_ptr< NoteTrackButtonHandle > &holder, const wxMouseState &state, const wxRect &rect, const std::shared_ptr< NoteTrack > &pTrack)
static void ReCreateVelocitySlider(struct ThemeChangeMessage)
static LWSlider * VelocitySlider(const wxRect &sliderRect, const NoteTrack *t, bool captured, wxWindow *pParent)
static void GetVelocityRect(const wxRect &rect, wxRect &dest)
static void GetMidiControlsRect(const wxRect &rect, wxRect &dest)
std::weak_ptr< NoteTrackButtonHandle > mClickHandle
std::weak_ptr< SoloButtonHandle > mSoloHandle
std::weak_ptr< VelocitySliderHandle > mVelocityHandle
static unsigned DefaultNoteTrackHeight()
const TCPLines & GetTCPLines() const override
std::weak_ptr< MuteButtonHandle > mMuteHandle
std::vector< UIHandlePtr > HitTest(const TrackPanelMouseState &state, const AudacityProject *pProject) override
A Track that is used for Midi notes. (Somewhat old code).
Definition: NoteTrack.h:78
bool IsVisibleChan(int c) const
Definition: NoteTrack.h:155
float GetVelocity() const
Definition: NoteTrack.h:124
PlayableTrackControls::InitMenuData * mpData
void OnChangeOctave(wxCommandEvent &)
Scrolls the note track up or down by an octave.
void InitUserData(void *pUserData) override
Called before the menu items are appended.
DECLARE_POPUP_MENU(NoteTrackMenuTable)
static NoteTrackMenuTable & Instance()
static NoteTrackRange & Get(const NoteTrack &track)
Allow mutative access to attached data of a const track.
void ShiftNoteRange(int offset)
Shifts all notes vertically by the given pitch.
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
Definition: Observer.h:199
static const TCPLines & StaticNoteTCPLines()
void ModifyState(bool bWantsAutoSave)
static ProjectHistory & Get(AudacityProject &project)
std::shared_ptr< Track > GetTrack() const
Definition: SliderHandle.h:36
static UIHandlePtr HitTest(std::weak_ptr< SoloButtonHandle > &holder, const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr< Track > &pTrack)
wxColour & Colour(int iIndex)
static TrackArtist * Get(TrackPanelDrawingContext &)
Definition: TrackArtist.cpp:82
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:110
static UIHandlePtr HitTest(std::weak_ptr< VelocitySliderHandle > &holder, const wxMouseState &state, const wxRect &rect, const std::shared_ptr< Track > &pTrack)
AUDACITY_DLL_API void GetSliderHorizontalBounds(const wxRect &rect, wxRect &dest)
static constexpr auto Margin
AUDACITY_DLL_API unsigned DefaultTrackHeight(const TCPLines &topLines)
AUDACITY_DLL_API std::pair< int, int > CalcItemY(const TCPLines &lines, unsigned iItem)
Definition: TrackInfo.cpp:61
void VelocitySliderDrawFunction(TrackPanelDrawingContext &context, const wxRect &rect, const Track *pTrack)
static void DrawLabelControls(const NoteTrack *pTrack, wxDC &dc, const wxRect &rect, int highlightedChannel)
void MidiControlsDrawFunction(TrackPanelDrawingContext &context, const wxRect &rect, const Track *pTrack)
void GetMidiControlsHorizontalBounds(const wxRect &rect, wxRect &dest)
void SliderDrawFunction(LWSlider *(*Selector)(const wxRect &sliderRect, const NoteTrack *t, bool captured, wxWindow *), wxDC *dc, const wxRect &rect, const Track *pTrack, wxWindow *pParent, bool captured, bool highlight)
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
For defining overrides of the method.
std::optional< PreferredSystemAppearance > appearance
Definition: Theme.h:111