mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-04 06:09:28 +02:00
... eliminating some duplication in tables for Wave and Note track controls, and freeing PlayableTrackButtonHandles from the big s.c.c, though in a cycle yet with PlayableTrackControls
313 lines
8.8 KiB
C++
313 lines
8.8 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
NoteTrackControls.cpp
|
|
|
|
Paul Licameli split from TrackPanel.cpp
|
|
|
|
**********************************************************************/
|
|
|
|
#include "../../../../Audacity.h" // for USE_* macros
|
|
#ifdef USE_MIDI
|
|
#include "NoteTrackControls.h"
|
|
|
|
#include "../../../../Experimental.h"
|
|
|
|
#include "NoteTrackButtonHandle.h"
|
|
|
|
#include "../../ui/PlayableTrackButtonHandles.h"
|
|
#include "NoteTrackSliderHandles.h"
|
|
|
|
#include "../../../../HitTestResult.h"
|
|
#include "../../../../TrackPanelMouseEvent.h"
|
|
#include "../../../../NoteTrack.h"
|
|
#include "../../../../widgets/PopupMenuTable.h"
|
|
#include "../../../../Project.h"
|
|
#include "../../../../ProjectHistory.h"
|
|
#include "../../../../RefreshCode.h"
|
|
#include "../../../../prefs/ThemePrefs.h"
|
|
|
|
#include <mutex>
|
|
#include <wx/frame.h>
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
NoteTrackControls::~NoteTrackControls()
|
|
{
|
|
}
|
|
|
|
std::vector<UIHandlePtr> NoteTrackControls::HitTest
|
|
(const TrackPanelMouseState & st,
|
|
const AudacityProject *pProject)
|
|
{
|
|
// Hits are mutually exclusive, results single
|
|
std::vector<UIHandlePtr> results;
|
|
const wxMouseState &state = st.state;
|
|
const wxRect &rect = st.rect;
|
|
if (state.ButtonIsDown(wxMOUSE_BTN_ANY)) {
|
|
auto track = std::static_pointer_cast<NoteTrack>(FindTrack());
|
|
auto result = [&]{
|
|
UIHandlePtr result;
|
|
if (NULL != (result = MuteButtonHandle::HitTest(
|
|
mMuteHandle, state, rect, pProject, track)))
|
|
return result;
|
|
|
|
if (NULL != (result = SoloButtonHandle::HitTest(
|
|
mSoloHandle, state, rect, pProject, track)))
|
|
return result;
|
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
|
if (NULL != (result = VelocitySliderHandle::HitTest(
|
|
mVelocityHandle, state, rect, track)))
|
|
return result;
|
|
if (NULL != (result = NoteTrackButtonHandle::HitTest(
|
|
mClickHandle, state, rect, track)))
|
|
return result;
|
|
#endif
|
|
return result;
|
|
}();
|
|
if (result) {
|
|
results.push_back(result);
|
|
return results;
|
|
}
|
|
}
|
|
|
|
return NoteTrackControlsBase::HitTest(st, pProject);
|
|
}
|
|
|
|
class NoteTrackMenuTable : public PopupMenuTable
|
|
{
|
|
NoteTrackMenuTable() : mpData(NULL) {}
|
|
DECLARE_POPUP_MENU(NoteTrackMenuTable);
|
|
|
|
public:
|
|
static NoteTrackMenuTable &Instance();
|
|
|
|
private:
|
|
void InitMenu(Menu*, void *pUserData) override
|
|
{
|
|
mpData = static_cast<NoteTrackControlsBase::InitMenuData*>(pUserData);
|
|
}
|
|
|
|
void DestroyMenu() override
|
|
{
|
|
mpData = nullptr;
|
|
}
|
|
|
|
NoteTrackControlsBase::InitMenuData *mpData;
|
|
|
|
void OnChangeOctave(wxCommandEvent &);
|
|
};
|
|
|
|
NoteTrackMenuTable &NoteTrackMenuTable::Instance()
|
|
{
|
|
static NoteTrackMenuTable instance;
|
|
return instance;
|
|
}
|
|
|
|
enum {
|
|
OnUpOctaveID = 30000,
|
|
OnDownOctaveID,
|
|
};
|
|
|
|
/// Scrolls the note track up or down by an octave
|
|
void NoteTrackMenuTable::OnChangeOctave(wxCommandEvent &event)
|
|
{
|
|
NoteTrack *const pTrack = static_cast<NoteTrack*>(mpData->pTrack);
|
|
|
|
wxASSERT(event.GetId() == OnUpOctaveID
|
|
|| event.GetId() == OnDownOctaveID);
|
|
|
|
const bool bDown = (OnDownOctaveID == event.GetId());
|
|
pTrack->ShiftNoteRange((bDown) ? -12 : 12);
|
|
|
|
AudacityProject *const project = ::GetActiveProject();
|
|
ProjectHistory::Get( *project )
|
|
.ModifyState(false);
|
|
mpData->result = RefreshCode::RefreshAll;
|
|
}
|
|
|
|
BEGIN_POPUP_MENU(NoteTrackMenuTable)
|
|
POPUP_MENU_SEPARATOR()
|
|
POPUP_MENU_ITEM(OnUpOctaveID, _("Up &Octave"), OnChangeOctave)
|
|
POPUP_MENU_ITEM(OnDownOctaveID, _("Down Octa&ve"), OnChangeOctave)
|
|
END_POPUP_MENU()
|
|
|
|
PopupMenuTable *NoteTrackControls::GetMenuExtension(Track *)
|
|
{
|
|
#if defined(USE_MIDI)
|
|
return &NoteTrackMenuTable::Instance();
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
// drawing related
|
|
#include "../../../../widgets/ASlider.h"
|
|
#include "../../../../TrackInfo.h"
|
|
#include "../../../../TrackPanelDrawingContext.h"
|
|
#include "../../../../ViewInfo.h"
|
|
|
|
using TCPLine = TrackInfo::TCPLine;
|
|
|
|
#ifdef USE_MIDI
|
|
enum : int {
|
|
// PRL: was it correct to include the margin?
|
|
kMidiCellWidth = ( ( kTrackInfoWidth + kLeftMargin ) / 4) - 2,
|
|
kMidiCellHeight = kTrackInfoBtnSize
|
|
};
|
|
#endif
|
|
|
|
#include "tracks/playabletrack/notetrack/ui/NoteTrackButtonHandle.h"
|
|
#include "tracks/playabletrack/notetrack/ui/NoteTrackSliderHandles.h"
|
|
|
|
namespace {
|
|
void GetMidiControlsHorizontalBounds( const wxRect &rect, wxRect &dest )
|
|
{
|
|
dest.x = rect.x + 1; // To center slightly
|
|
// PRL: TODO: kMidiCellWidth is defined in terms of the other constant
|
|
// kTrackInfoWidth but I am trying to avoid use of that constant.
|
|
// Can cell width be computed from dest.width instead?
|
|
dest.width = kMidiCellWidth * 4;
|
|
}
|
|
|
|
void SliderDrawFunction
|
|
( LWSlider *(*Selector)
|
|
(const wxRect &sliderRect, const NoteTrack *t, bool captured, wxWindow*),
|
|
wxDC *dc, const wxRect &rect, const Track *pTrack,
|
|
bool captured, bool highlight )
|
|
{
|
|
wxRect sliderRect = rect;
|
|
TrackInfo::GetSliderHorizontalBounds( rect.GetTopLeft(), sliderRect );
|
|
auto nt = static_cast<const NoteTrack*>( pTrack );
|
|
Selector( sliderRect, nt, captured, nullptr )->OnPaint(*dc, highlight);
|
|
}
|
|
|
|
void VelocitySliderDrawFunction
|
|
( TrackPanelDrawingContext &context,
|
|
const wxRect &rect, const Track *pTrack )
|
|
{
|
|
auto dc = &context.dc;
|
|
auto target = dynamic_cast<VelocitySliderHandle*>( context.target.get() );
|
|
bool hit = target && target->GetTrack().get() == pTrack;
|
|
bool captured = hit && target->IsClicked();
|
|
SliderDrawFunction(
|
|
&NoteTrackControls::VelocitySlider, dc, rect, pTrack, captured, hit);
|
|
}
|
|
|
|
void MidiControlsDrawFunction
|
|
( TrackPanelDrawingContext &context,
|
|
const wxRect &rect, const Track *pTrack )
|
|
{
|
|
auto target = dynamic_cast<NoteTrackButtonHandle*>( context.target.get() );
|
|
bool hit = target && target->GetTrack().get() == pTrack;
|
|
auto channel = hit ? target->GetChannel() : -1;
|
|
auto &dc = context.dc;
|
|
wxRect midiRect = rect;
|
|
GetMidiControlsHorizontalBounds(rect, midiRect);
|
|
NoteTrack::DrawLabelControls
|
|
( static_cast<const NoteTrack *>(pTrack), dc, midiRect, channel );
|
|
}
|
|
}
|
|
|
|
static const struct NoteTrackTCPLines
|
|
: TCPLines { NoteTrackTCPLines() {
|
|
(TCPLines&)*this =
|
|
NoteTrackControlsBase::StaticTCPLines();
|
|
insert( end(), {
|
|
{ TCPLine::kItemMidiControlsRect, kMidiCellHeight * 4, 0,
|
|
MidiControlsDrawFunction },
|
|
{ TCPLine::kItemVelocity, kTrackInfoSliderHeight, kTrackInfoSliderExtra,
|
|
VelocitySliderDrawFunction },
|
|
} );
|
|
} } noteTrackTCPLines;
|
|
|
|
void NoteTrackControls::GetVelocityRect(const wxPoint &topleft, wxRect & dest)
|
|
{
|
|
TrackInfo::GetSliderHorizontalBounds( topleft, dest );
|
|
auto results = CalcItemY( noteTrackTCPLines, TCPLine::kItemVelocity );
|
|
dest.y = topleft.y + results.first;
|
|
dest.height = results.second;
|
|
}
|
|
|
|
void NoteTrackControls::GetMidiControlsRect(const wxRect & rect, wxRect & dest)
|
|
{
|
|
GetMidiControlsHorizontalBounds( rect, dest );
|
|
auto results = TrackInfo::CalcItemY(
|
|
noteTrackTCPLines, TCPLine::kItemMidiControlsRect );
|
|
dest.y = rect.y + results.first;
|
|
dest.height = results.second;
|
|
}
|
|
|
|
unsigned NoteTrackControls::DefaultNoteTrackHeight()
|
|
{
|
|
return TrackInfo::DefaultTrackHeight( noteTrackTCPLines );
|
|
}
|
|
|
|
const TCPLines &NoteTrackControls::GetTCPLines() const
|
|
{
|
|
return noteTrackTCPLines;
|
|
};
|
|
|
|
#endif
|
|
|
|
namespace {
|
|
|
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
|
std::unique_ptr<LWSlider>
|
|
gVelocityCaptured
|
|
, gVelocity
|
|
;
|
|
#endif
|
|
|
|
}
|
|
|
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
|
LWSlider * NoteTrackControls::VelocitySlider
|
|
(const wxRect &sliderRect, const NoteTrack *t, bool captured, wxWindow *pParent)
|
|
{
|
|
static std::once_flag flag;
|
|
std::call_once( flag, [] {
|
|
wxCommandEvent dummy;
|
|
ReCreateVelocitySlider( dummy );
|
|
wxTheApp->Bind(EVT_THEME_CHANGE, ReCreateVelocitySlider);
|
|
} );
|
|
|
|
wxPoint pos = sliderRect.GetPosition();
|
|
float velocity = t ? t->GetVelocity() : 0.0;
|
|
|
|
gVelocity->Move(pos);
|
|
gVelocity->Set(velocity);
|
|
gVelocityCaptured->Move(pos);
|
|
gVelocityCaptured->Set(velocity);
|
|
|
|
auto slider = (captured ? gVelocityCaptured : gVelocity).get();
|
|
slider->SetParent( pParent ? pParent :
|
|
FindProjectFrame( ::GetActiveProject() ) );
|
|
return slider;
|
|
}
|
|
#endif
|
|
|
|
void NoteTrackControls::ReCreateVelocitySlider( wxEvent &evt )
|
|
{
|
|
evt.Skip();
|
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
|
wxPoint point{ 0, 0 };
|
|
wxRect sliderRect;
|
|
GetVelocityRect(point, sliderRect);
|
|
|
|
/* i18n-hint: Title of the Velocity slider, used to adjust the volume of note tracks */
|
|
gVelocity = std::make_unique<LWSlider>(nullptr, _("Velocity"),
|
|
wxPoint(sliderRect.x, sliderRect.y),
|
|
wxSize(sliderRect.width, sliderRect.height),
|
|
VEL_SLIDER);
|
|
gVelocity->SetDefaultValue(0.0);
|
|
gVelocityCaptured = std::make_unique<LWSlider>(nullptr, _("Velocity"),
|
|
wxPoint(sliderRect.x, sliderRect.y),
|
|
wxSize(sliderRect.width, sliderRect.height),
|
|
VEL_SLIDER);
|
|
gVelocityCaptured->SetDefaultValue(0.0);
|
|
#else
|
|
pParent;
|
|
#endif
|
|
}
|