1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-04 06:09:28 +02:00
audacity/src/tracks/playabletrack/notetrack/ui/NoteTrackControls.cpp
Paul Licameli 87ef97abe2 Move code to PlayableTrackControls...
... 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
2019-06-19 19:02:56 -04:00

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
}