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 + 1; // To center slightly
160 // PRL: TODO: kMidiCellWidth is defined in terms of the other constant
161 // kTrackInfoWidth but I am trying to avoid use of that constant.
162 // Can cell width be computed from dest.width instead?
163 dest.width = kMidiCellWidth * 4;
164}
165
167( LWSlider *(*Selector)
168 (const wxRect &sliderRect, const NoteTrack *t, bool captured, wxWindow*),
169 wxDC *dc, const wxRect &rect, const Track *pTrack,
170 wxWindow *pParent,
171 bool captured, bool highlight )
172{
173 wxRect sliderRect = rect;
174 CommonTrackInfo::GetSliderHorizontalBounds( rect.GetTopLeft(), sliderRect );
175 auto nt = static_cast<const NoteTrack*>( pTrack );
176 Selector( sliderRect, nt, captured, pParent )->OnPaint(*dc, highlight);
177}
178
180( TrackPanelDrawingContext &context,
181 const wxRect &rect, const Track *pTrack )
182{
183 auto dc = &context.dc;
184 auto target = dynamic_cast<VelocitySliderHandle*>( context.target.get() );
185 bool hit = target && target->GetTrack().get() == pTrack;
186 bool captured = hit && target->IsDragging();
187
188 const auto artist = TrackArtist::Get( context );
189 auto pParent = FindProjectFrame( artist->parent->GetProject() );
190
192 &NoteTrackControls::VelocitySlider, dc, rect, pTrack,
193 pParent, captured, hit);
194}
195
196// Draws the midi channel toggle buttons within the given rect.
197// The rect should be evenly divisible by 4 on both axes.
199( const NoteTrack *pTrack, wxDC & dc, const wxRect &rect, int highlightedChannel )
200{
201 dc.SetTextForeground(theTheme.Colour(clrLabelTrackText));
202 wxASSERT_MSG(rect.width % 4 == 0, "Midi channel control rect width must be divisible by 4");
203 wxASSERT_MSG(rect.height % 4 == 0, "Midi channel control rect height must be divisible by 4");
204
205 auto cellWidth = rect.width / 4;
206 auto cellHeight = rect.height / 4;
207
208 wxRect box;
209 for (int row = 0; row < 4; row++) {
210 for (int col = 0; col < 4; col++) {
211 // chanName is the "external" channel number (1-16)
212 // used by AColor and button labels
213 int chanName = row * 4 + col + 1;
214
215 box.x = rect.x + col * cellWidth;
216 box.y = rect.y + row * cellHeight;
217 box.width = cellWidth;
218 box.height = cellHeight;
219
220 bool visible = pTrack ? pTrack->IsVisibleChan(chanName - 1) : true;
221 if (visible) {
222 // highlightedChannel counts 0 based
223 if ( chanName == highlightedChannel + 1 )
224 AColor::LightMIDIChannel(&dc, chanName);
225 else
226 AColor::MIDIChannel(&dc, chanName);
227 dc.DrawRectangle(box);
228// two choices: channel is enabled (to see and play) when button is in
229// "up" position (original Audacity style) or in "down" position
230//
231#define CHANNEL_ON_IS_DOWN 1
232#if CHANNEL_ON_IS_DOWN
233 AColor::DarkMIDIChannel(&dc, chanName);
234#else
235 AColor::LightMIDIChannel(&dc, chanName);
236#endif
237 AColor::Line(dc, box.x, box.y, box.x + box.width - 1, box.y);
238 AColor::Line(dc, box.x, box.y, box.x, box.y + box.height - 1);
239
240#if CHANNEL_ON_IS_DOWN
241 AColor::LightMIDIChannel(&dc, chanName);
242#else
243 AColor::DarkMIDIChannel(&dc, chanName);
244#endif
245 AColor::Line(dc,
246 box.x + box.width - 1, box.y,
247 box.x + box.width - 1, box.y + box.height - 1);
248 AColor::Line(dc,
249 box.x, box.y + box.height - 1,
250 box.x + box.width - 1, box.y + box.height - 1);
251 } else {
252 if ( chanName == highlightedChannel + 1 )
253 AColor::LightMIDIChannel(&dc, chanName);
254 else
255 AColor::MIDIChannel(&dc, 0);
256 dc.DrawRectangle(box);
257#if CHANNEL_ON_IS_DOWN
259#else
261#endif
262 AColor::Line(dc, box.x, box.y, box.x + box.width - 1, box.y);
263 AColor::Line(dc, box.x, box.y, box.x, box.y + box.height - 1);
264
265#if CHANNEL_ON_IS_DOWN
267#else
269#endif
270 AColor::Line(dc,
271 box.x + box.width - 1, box.y,
272 box.x + box.width - 1, box.y + box.height - 1);
273 AColor::Line(dc,
274 box.x, box.y + box.height - 1,
275 box.x + box.width - 1, box.y + box.height - 1);
276
277 }
278
279 wxString text;
280 wxCoord w;
281 wxCoord h;
282
283 text.Printf(wxT("%d"), chanName);
284 dc.GetTextExtent(text, &w, &h);
285
286 dc.DrawText(text, box.x + (box.width - w) / 2, box.y + (box.height - h) / 2);
287 }
288 }
289 dc.SetTextForeground(theTheme.Colour(clrTrackPanelText));
290 AColor::MIDIChannel(&dc, 0); // always return with gray color selected
291}
292
294( TrackPanelDrawingContext &context,
295 const wxRect &rect, const Track *pTrack )
296{
297 auto target = dynamic_cast<NoteTrackButtonHandle*>( context.target.get() );
298 bool hit = target && target->GetTrack().get() == pTrack;
299 auto channel = hit ? target->GetChannel() : -1;
300 auto &dc = context.dc;
301 wxRect midiRect = rect;
302 GetMidiControlsHorizontalBounds(rect, midiRect);
304 ( static_cast<const NoteTrack *>(pTrack), dc, midiRect, channel );
305}
306}
307
308static const struct NoteTrackTCPLines
310 *static_cast<TCPLines*>(this) =
312 insert( end(), {
317 } );
319
320void NoteTrackControls::GetVelocityRect(const wxPoint &topleft, wxRect & dest)
321{
324 dest.y = topleft.y + results.first;
325 dest.height = results.second;
326}
327
328void NoteTrackControls::GetMidiControlsRect(const wxRect & rect, wxRect & dest)
329{
331 auto results = TrackInfo::CalcItemY(
333 dest.y = rect.y + results.first;
334 dest.height = results.second;
335}
336
338{
340}
341
343{
344 return noteTrackTCPLines;
345};
346
347namespace {
348
349 std::unique_ptr<LWSlider>
352 ;
353
354}
355
357(const wxRect &sliderRect, const NoteTrack *t, bool captured, wxWindow *pParent)
358{
359 static std::once_flag flag;
360 std::call_once( flag, []{ ReCreateVelocitySlider({}); });
362
363 wxPoint pos = sliderRect.GetPosition();
364 float velocity = t ? t->GetVelocity() : 0.0;
365
366 gVelocity->Move(pos);
367 gVelocity->Set(velocity);
368 gVelocityCaptured->Move(pos);
369 gVelocityCaptured->Set(velocity);
370
371 auto slider = (captured ? gVelocityCaptured : gVelocity).get();
372 slider->SetParent( pParent );
373 return slider;
374}
375
377{
378 if (message.appearance)
379 return;
380 wxPoint point{ 0, 0 };
381 wxRect sliderRect;
382 GetVelocityRect(point, sliderRect);
383
384 /* i18n-hint: Title of the Velocity slider, used to adjust the volume of note tracks */
385 gVelocity = std::make_unique<LWSlider>(nullptr, XO("Velocity"),
386 wxPoint(sliderRect.x, sliderRect.y),
387 wxSize(sliderRect.width, sliderRect.height),
388 VEL_SLIDER);
389 gVelocity->SetDefaultValue(0.0);
390 gVelocityCaptured = std::make_unique<LWSlider>(nullptr, XO("Velocity"),
391 wxPoint(sliderRect.x, sliderRect.y),
392 wxSize(sliderRect.width, sliderRect.height),
393 VEL_SLIDER);
394 gVelocityCaptured->SetDefaultValue(0.0);
395}
396
399 return [](NoteTrack &track) {
400 return std::make_shared<NoteTrackControls>( track.SharedPointer() );
401 };
402}
403
404#include "../../../ui/ChannelView.h"
405
408 return [](NoteTrack &) {
410 };
411}
412
413#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
@ OnUpOctaveID
@ OnDownOctaveID
BeginSection("Basic")
@ kMidiCellWidth
@ kMidiCellHeight
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
@ kTrackInfoSliderExtra
Definition: ViewInfo.h:101
@ kTrackInfoBtnSize
Definition: ViewInfo.h:96
@ kTrackInfoSliderHeight
Definition: ViewInfo.h:98
static std::once_flag flag
@ kTrackInfoWidth
Definition: ZoomInfo.h:30
@ kLeftMargin
Definition: ZoomInfo.h:27
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:187
static void LightMIDIChannel(wxDC *dc, int channel)
Definition: AColor.cpp:669
static void MIDIChannel(wxDC *dc, int channel)
Definition: AColor.cpp:653
static void DarkMIDIChannel(wxDC *dc, int channel)
Definition: AColor.cpp:687
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 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
static void GetVelocityRect(const wxPoint &topleft, wxRect &dest)
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:69
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 unsigned DefaultTrackHeight(const TCPLines &topLines)
AUDACITY_DLL_API void GetSliderHorizontalBounds(const wxPoint &topleft, wxRect &dest)
AUDACITY_DLL_API std::pair< int, int > CalcItemY(const TCPLines &lines, unsigned iItem)
Definition: TrackInfo.cpp:79
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:112