1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-16 08:34:10 +02:00

Use TypeSwitch and track_cast in TrackPanel

This commit is contained in:
Paul Licameli 2017-04-25 09:24:49 -04:00
parent e17c4acd5d
commit fd10ed26cd
7 changed files with 248 additions and 235 deletions

View File

@ -957,13 +957,22 @@ int totalTCPLines( const TCPLines &lines, bool omitLastExtra )
const TCPLines &getTCPLines( const Track &track )
{
auto lines = track.TypeSwitch< TCPLines * >(
#ifdef USE_MIDI
if ( track.GetKind() == Track::Note )
return noteTrackTCPLines;
[](const NoteTrack*){
return &noteTrackTCPLines;
},
#endif
[](const WaveTrack*){
return &waveTrackTCPLines;
},
[](const Track*){
return &commonTrackTCPLines;
}
);
if ( track.GetKind() == Track::Wave )
return waveTrackTCPLines;
if (lines)
return *lines;
return commonTrackTCPLines;
}
@ -1719,7 +1728,7 @@ void TrackPanel::DrawOutside
const Track * t, const wxRect & rec)
{
auto dc = &context.dc;
bool bIsWave = (t->GetKind() == Track::Wave);
const auto wt = track_cast<const WaveTrack*>(t);
// Draw things that extend right of track control panel
{
@ -1735,7 +1744,7 @@ void TrackPanel::DrawOutside
int labelw = GetLabelWidth();
int vrul = GetVRulerOffset();
mTrackInfo.DrawBackground(dc, rect, t->GetSelected(), bIsWave, labelw, vrul);
mTrackInfo.DrawBackground(dc, rect, t->GetSelected(), (wt != nullptr), labelw, vrul);
// Vaughan, 2010-08-24: No longer doing this.
// Draw sync-lock tiles in ruler area.

View File

@ -373,26 +373,26 @@ wxAccStatus TrackPanelAx::GetName( int childId, wxString* name )
name->Printf(_("Track %d"), TrackNum( t ) );
}
if (t->GetKind() == Track::Label)
{
/* i18n-hint: This is for screen reader software and indicates that
this is a Label track.*/
name->Append( wxT(" ") + wxString(_("Label Track")));
}
else if (t->GetKind() == Track::Time)
{
/* i18n-hint: This is for screen reader software and indicates that
this is a Time track.*/
name->Append( wxT(" ") + wxString(_("Time Track")));
}
t->TypeSwitch(
[&](const LabelTrack *) {
/* i18n-hint: This is for screen reader software and indicates that
this is a Label track.*/
name->Append( wxT(" ") + wxString(_("Label Track")));
},
[&](const TimeTrack *) {
/* i18n-hint: This is for screen reader software and indicates that
this is a Time track.*/
name->Append( wxT(" ") + wxString(_("Time Track")));
}
#ifdef USE_MIDI
else if (t->GetKind() == Track::Note)
{
/* i18n-hint: This is for screen reader software and indicates that
this is a Note track.*/
name->Append( wxT(" ") + wxString(_("Note Track")));
}
,
[&](const NoteTrack *) {
/* i18n-hint: This is for screen reader software and indicates that
this is a Note track.*/
name->Append( wxT(" ") + wxString(_("Note Track")));
}
#endif
);
// LLL: Remove these during "refactor"
auto pt = dynamic_cast<PlayableTrack *>(t.get());

View File

@ -284,63 +284,60 @@ void StretchHandle::Stretch(AudacityProject *pProject, int mouseXCoordinate, int
if (pTrack == NULL && mpTrack != NULL)
pTrack = mpTrack.get();
if (!pTrack || pTrack->GetKind() != Track::Note) {
return;
}
if (pTrack) pTrack->TypeSwitch( [&](NoteTrack *pNt) {
double moveto =
std::max(0.0, viewInfo.PositionToTime(mouseXCoordinate, trackLeftEdge));
NoteTrack *pNt = static_cast<NoteTrack *>(pTrack);
double moveto =
std::max(0.0, viewInfo.PositionToTime(mouseXCoordinate, trackLeftEdge));
double dur, left_dur, right_dur;
double dur, left_dur, right_dur;
// check to make sure tempo is not higher than 20 beats per second
// (In principle, tempo can be higher, but not infinity.)
double minPeriod = 0.05; // minimum beat period
// check to make sure tempo is not higher than 20 beats per second
// (In principle, tempo can be higher, but not infinity.)
double minPeriod = 0.05; // minimum beat period
// make sure target duration is not too short
// Take quick exit if so, without changing the selection.
auto t0 = mStretchState.mBeat0.first;
auto t1 = mStretchState.mBeat1.first;
switch ( mStretchState.mMode ) {
case stretchLeft: {
dur = t1 - moveto;
if (dur < mStretchState.mRightBeats * minPeriod)
return;
pNt->StretchRegion
( mStretchState.mBeat0, mStretchState.mBeat1, dur );
pNt->Offset( moveto - t0 );
mStretchState.mBeat0.first = moveto;
viewInfo.selectedRegion.setT0(moveto);
break;
}
case stretchRight: {
dur = moveto - t0;
if (dur < mStretchState.mLeftBeats * minPeriod)
return;
pNt->StretchRegion
( mStretchState.mBeat0, mStretchState.mBeat1, dur );
viewInfo.selectedRegion.setT1(moveto);
mStretchState.mBeat1.first = moveto;
break;
}
case stretchCenter: {
moveto = std::max(t0, std::min(t1, moveto));
left_dur = moveto - t0;
right_dur = t1 - moveto;
if ( left_dur < mStretchState.mLeftBeats * minPeriod ||
right_dur < mStretchState.mRightBeats * minPeriod )
return;
pNt->StretchRegion
( mStretchState.mBeatCenter, mStretchState.mBeat1, right_dur );
pNt->StretchRegion
( mStretchState.mBeat0, mStretchState.mBeatCenter, left_dur );
mStretchState.mBeatCenter.first = moveto;
break;
}
default:
wxASSERT(false);
break;
}
// make sure target duration is not too short
// Take quick exit if so, without changing the selection.
auto t0 = mStretchState.mBeat0.first;
auto t1 = mStretchState.mBeat1.first;
switch ( mStretchState.mMode ) {
case stretchLeft: {
dur = t1 - moveto;
if (dur < mStretchState.mRightBeats * minPeriod)
return;
pNt->StretchRegion
( mStretchState.mBeat0, mStretchState.mBeat1, dur );
pNt->Offset( moveto - t0 );
mStretchState.mBeat0.first = moveto;
viewInfo.selectedRegion.setT0(moveto);
break;
}
case stretchRight: {
dur = moveto - t0;
if (dur < mStretchState.mLeftBeats * minPeriod)
return;
pNt->StretchRegion
( mStretchState.mBeat0, mStretchState.mBeat1, dur );
viewInfo.selectedRegion.setT1(moveto);
mStretchState.mBeat1.first = moveto;
break;
}
case stretchCenter: {
moveto = std::max(t0, std::min(t1, moveto));
left_dur = moveto - t0;
right_dur = t1 - moveto;
if ( left_dur < mStretchState.mLeftBeats * minPeriod ||
right_dur < mStretchState.mRightBeats * minPeriod )
return;
pNt->StretchRegion
( mStretchState.mBeatCenter, mStretchState.mBeat1, right_dur );
pNt->StretchRegion
( mStretchState.mBeat0, mStretchState.mBeatCenter, left_dur );
mStretchState.mBeatCenter.first = moveto;
break;
}
default:
wxASSERT(false);
break;
}
});
}
#endif

View File

@ -178,45 +178,52 @@ UIHandle::Result EnvelopeHandle::Click
const ViewInfo &viewInfo = pProject->GetViewInfo();
const auto pTrack = static_cast<Track*>(evt.pCell.get());
if (pTrack &&
pTrack->GetKind() == Track::Wave) {
WaveTrack *const wt = static_cast<WaveTrack*>(pTrack);
if (wt->GetDisplay() != WaveTrack::Waveform)
unsigned result = Cancelled;
if (pTrack)
result = pTrack->TypeSwitch< decltype(RefreshNone) >(
[&](WaveTrack *wt) {
if (wt->GetDisplay() != WaveTrack::Waveform)
return Cancelled;
if (!mEnvelope)
return Cancelled;
mLog = !wt->GetWaveformSettings().isLinear();
wt->GetDisplayBounds(&mLower, &mUpper);
mdBRange = wt->GetWaveformSettings().dBRange;
mEnvelopeEditor =
std::make_unique< EnvelopeEditor >( *mEnvelope, true );
mEnvelopeEditorRight.reset();
// Assume linked track is wave or null
auto partner = static_cast<WaveTrack*>(wt->GetLink());
if (partner)
{
auto clickedEnvelope = partner->GetEnvelopeAtX(event.GetX());
if (clickedEnvelope)
mEnvelopeEditorRight =
std::make_unique< EnvelopeEditor >( *clickedEnvelope, true );
}
return RefreshNone;
},
[&](TimeTrack *tt) {
if (!mEnvelope)
return Cancelled;
GetTimeTrackData( *pProject, *tt, mdBRange, mLog, mLower, mUpper);
mEnvelopeEditor =
std::make_unique< EnvelopeEditor >( *mEnvelope, false );
mEnvelopeEditorRight.reset();
return RefreshNone;
},
[](Track *) {
return Cancelled;
if (!mEnvelope)
return Cancelled;
mLog = !wt->GetWaveformSettings().isLinear();
wt->GetDisplayBounds(&mLower, &mUpper);
mdBRange = wt->GetWaveformSettings().dBRange;
mEnvelopeEditor =
std::make_unique< EnvelopeEditor >( *mEnvelope, true );
mEnvelopeEditorRight.reset();
// Assume linked track is wave or null
auto partner = static_cast<WaveTrack*>(wt->GetLink());
if (partner)
{
auto clickedEnvelope = partner->GetEnvelopeAtX(event.GetX());
if (clickedEnvelope)
mEnvelopeEditorRight =
std::make_unique< EnvelopeEditor >( *clickedEnvelope, true );
}
}
else if (pTrack &&
pTrack->GetKind() == Track::Time)
{
TimeTrack *const tt = static_cast<TimeTrack*>(pTrack);
if (!mEnvelope)
return Cancelled;
GetTimeTrackData( *pProject, *tt, mdBRange, mLog, mLower, mUpper);
mEnvelopeEditor =
std::make_unique< EnvelopeEditor >( *mEnvelope, false );
mEnvelopeEditorRight.reset();
}
else
return Cancelled;
);
if (result & Cancelled)
return result;
mRect = evt.rect;

View File

@ -85,23 +85,21 @@ void PlayIndicatorOverlayBase::Draw(OverlayPanel &panel, wxDC &dc)
for ( const auto &data : tp->Cells() )
{
Track *const pTrack = dynamic_cast<Track*>(data.first.get());
if (!pTrack)
continue;
// Don't draw the indicator in label tracks
if (pTrack->GetKind() == Track::Label)
{
continue;
}
// Draw the NEW indicator in its NEW location
// AColor::Line includes both endpoints so use GetBottom()
const wxRect &rect = data.second;
AColor::Line(dc,
mLastIndicatorX,
rect.GetTop(),
mLastIndicatorX,
rect.GetBottom());
if (pTrack) pTrack->TypeSwitch(
[](LabelTrack *) {
// Don't draw the indicator in label tracks
},
[&](Track *) {
// Draw the NEW indicator in its NEW location
// AColor::Line includes both endpoints so use GetBottom()
const wxRect &rect = data.second;
AColor::Line(dc,
mLastIndicatorX,
rect.GetTop(),
mLastIndicatorX,
rect.GetBottom());
}
);
}
}
else if(auto ruler = dynamic_cast<AdornedRulerPanel*>(&panel)) {

View File

@ -65,14 +65,13 @@ namespace
wxASSERT(t);
//For OD regions, we need to override and display the percent complete for this task.
//first, make sure it's a wavetrack.
if (t->GetKind() != Track::Wave)
return;
//see if the wavetrack exists in the ODManager (if the ODManager exists)
if (!ODManager::IsInstanceCreated())
return;
//ask the wavetrack for the corresponding tip - it may not change tip, but that's fine.
ODManager::Instance()->FillTipForWaveTrack(static_cast<const WaveTrack*>(t), tip);
return;
t->TypeSwitch( [&](const WaveTrack *wt) {
//see if the wavetrack exists in the ODManager (if the ODManager exists)
if (!ODManager::IsInstanceCreated())
return;
//ask the wavetrack for the corresponding tip - it may not change tip, but that's fine.
ODManager::Instance()->FillTipForWaveTrack(wt, tip);
});
}
/// Converts a frequency to screen y position.
@ -125,16 +124,12 @@ namespace
// This returns true if we're a spectral editing track.
inline bool isSpectralSelectionTrack(const Track *pTrack) {
if (pTrack &&
pTrack->GetKind() == Track::Wave) {
const WaveTrack *const wt = static_cast<const WaveTrack*>(pTrack);
return pTrack && pTrack->TypeSwitch< bool >( [&](const WaveTrack *wt) {
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
const int display = wt->GetDisplay();
return (display == WaveTrack::Spectrum) && settings.SpectralSelectionEnabled();
}
else {
return false;
}
return (display == WaveTrack::Spectrum) &&
settings.SpectralSelectionEnabled();
});
}
enum SelectionBoundary {
@ -542,19 +537,23 @@ UIHandle::Result SelectHandle::Click
TrackPanel *const trackPanel = pProject->GetTrackPanel();
if( pTrack->GetKind() == Track::Label &&
event.LeftDown() &&
event.ControlDown() ){
// We should reach this, only in default of other hits on glyphs or
// text boxes.
bool bShift = event.ShiftDown();
bool unsafe = pProject->IsAudioActive();
GetMenuCommandHandler(*pProject)
.HandleListSelection(*pProject, pTrack, bShift, true, !unsafe);
bool selectChange = (
event.LeftDown() &&
event.ControlDown() &&
pTrack->TypeSwitch<bool>( [&](LabelTrack *){
// We should reach this, only in default of other hits on glyphs or
// text boxes.
bool bShift = event.ShiftDown();
bool unsafe = pProject->IsAudioActive();
GetMenuCommandHandler(*pProject)
.HandleListSelection(*pProject, pTrack, bShift, true, !unsafe);
return true;
} )
);
if ( selectChange )
// Do not start a drag
return RefreshAll | Cancelled;
}
auto &selectionState = pProject->GetSelectionState();
if (event.LeftDClick() && !event.ShiftDown()) {
TrackList *const trackList = pProject->GetTracks();
@ -571,14 +570,13 @@ UIHandle::Result SelectHandle::Click
// Special case: if we're over a clip in a WaveTrack,
// select just that clip
if (pTrack->GetKind() == Track::Wave) {
WaveTrack *const wt = static_cast<WaveTrack *>(pTrack);
pTrack->TypeSwitch( [&] ( WaveTrack *wt ) {
WaveClip *const selectedClip = wt->GetClipAtX(event.m_x);
if (selectedClip) {
viewInfo.selectedRegion.setTimes(
selectedClip->GetOffset(), selectedClip->GetEndTime());
}
}
} );
pProject->ModifyState(false);
@ -776,11 +774,10 @@ UIHandle::Result SelectHandle::Click
( *pTrack, true, true, pMixerBoard );
trackPanel->SetFocusedTrack(pTrack);
//On-Demand: check to see if there is an OD thing associated with this track.
if (pTrack->GetKind() == Track::Wave) {
pTrack->TypeSwitch( [&](WaveTrack *wt) {
if(ODManager::IsInstanceCreated())
ODManager::Instance()->DemandTrackUpdate
(static_cast<WaveTrack*>(pTrack),mSelStart);
}
ODManager::Instance()->DemandTrackUpdate(wt,mSelStart);
});
Connect(pProject);
return RefreshAll | UpdateSelection;
@ -1201,9 +1198,11 @@ void SelectHandle::AssignSelection
viewInfo.selectedRegion.setTimes(sel0, sel1);
//On-Demand: check to see if there is an OD thing associated with this track. If so we want to update the focal point for the task.
if (pTrack && (pTrack->GetKind() == Track::Wave) && ODManager::IsInstanceCreated())
ODManager::Instance()->DemandTrackUpdate
(static_cast<WaveTrack*>(pTrack),sel0); //sel0 is sometimes less than mSelStart
if (pTrack && ODManager::IsInstanceCreated())
pTrack->TypeSwitch( [&](WaveTrack *wt) {
ODManager::Instance()->DemandTrackUpdate(wt, sel0);
//sel0 is sometimes less than mSelStart
});
}
void SelectHandle::StartFreqSelection(ViewInfo &viewInfo,

View File

@ -15,6 +15,7 @@ Paul Licameli split from TrackPanel.cpp
#include "TrackControls.h"
#include "../../AColor.h"
#include "../../HitTestResult.h"
#include "../../NoteTrack.h"
#include "../../Project.h"
#include "../../RefreshCode.h"
#include "../../TrackPanelMouseEvent.h"
@ -101,38 +102,40 @@ namespace
void AddClipsToCaptured
( ClipMoveState &state, Track *t, double t0, double t1 )
{
auto &clips = state.capturedClipArray;
bool exclude = true;
if (t->GetKind() == Track::Wave)
{
exclude = false;
for(const auto &clip: static_cast<WaveTrack*>(t)->GetClips())
if ( ! clip->AfterClip(t0) && ! clip->BeforeClip(t1) &&
// Avoid getting clips that were already captured
! std::any_of( clips.begin(), clips.end(),
[&](const TrackClip &c) { return c.clip == clip.get(); } ) )
clips.emplace_back( t, clip.get() );
}
else
{
// This handles label tracks rather heavy-handedly -- it would be nice to
// treat individual labels like clips
// Avoid adding a track twice
if( ! std::any_of( clips.begin(), clips.end(),
[&](const TrackClip &c) { return c.track == t; } ) ) {
#ifdef USE_MIDI
auto &clips = state.capturedClipArray;
t->TypeSwitch(
[&](WaveTrack *wt) {
exclude = false;
for(const auto &clip: wt->GetClips())
if ( ! clip->AfterClip(t0) && ! clip->BeforeClip(t1) &&
// Avoid getting clips that were already captured
! std::any_of( clips.begin(), clips.end(),
[&](const TrackClip &c) { return c.clip == clip.get(); } ) )
clips.emplace_back( t, clip.get() );
},
#ifdef USE_MIDI
[&](NoteTrack *, const Track::Fallthrough &fallthrough){
// do not add NoteTrack if the data is outside of time bounds
if (t->GetKind() == Track::Note) {
if (t->GetEndTime() < t0 || t->GetStartTime() > t1)
return;
if (t->GetEndTime() < t0 || t->GetStartTime() > t1)
return;
else
fallthrough();
},
#endif
[&](Track *t) {
// This handles label tracks rather heavy-handedly --
// it would be nice to
// treat individual labels like clips
// Avoid adding a track twice
if( !std::any_of( clips.begin(), clips.end(),
[&](const TrackClip &c) { return c.track == t; } ) ) {
clips.emplace_back( t, nullptr );
}
#endif
clips.emplace_back( t, nullptr );
}
}
if ( exclude )
);
if (exclude)
state.trackExclusions.push_back(t);
}
@ -273,14 +276,13 @@ void TimeShiftHandle::CreateListOfCapturedClips
}
#ifdef USE_MIDI
// Capture additional clips from NoteTracks
Track *nt = trackClip.track;
if (nt->GetKind() == Track::Note) {
trackClip.track->TypeSwitch( [&](NoteTrack *nt) {
// Iterate over sync-lock group tracks.
SyncLockedTracksIterator git( &trackList );
for (Track *t = git.StartWith(nt); t; t = git.Next())
AddClipsToCaptured
( state, t, nt->GetStartTime(), nt->GetEndTime() );
}
});
#endif
}
}
@ -376,24 +378,21 @@ UIHandle::Result TimeShiftHandle::Click
bool ok = true;
bool captureClips = false;
if (!event.ShiftDown()) {
WaveTrack *wt = pTrack->GetKind() == Track::Wave
? static_cast<WaveTrack*>(pTrack.get()) : nullptr;
if (wt)
{
if (nullptr ==
(mClipMoveState.capturedClip = wt->GetClipAtX(event.m_x)))
ok = false;
else
captureClips = true;
}
if (!event.ShiftDown())
pTrack->TypeSwitch(
[&](WaveTrack *wt) {
if (nullptr ==
(mClipMoveState.capturedClip = wt->GetClipAtX(event.m_x)))
ok = false;
else
captureClips = true;
},
#ifdef USE_MIDI
else if (pTrack->GetKind() == Track::Note)
{
captureClips = true;
}
[&](NoteTrack *) {
captureClips = true;
}
#endif
}
);
if ( ! ok )
return Cancelled;
@ -436,12 +435,11 @@ namespace {
viewInfo.PositionToTime(state.mMouseClickX);
double clipLeft = 0, clipRight = 0;
if (track.GetKind() == Track::Wave) {
WaveTrack *const mtw = static_cast<WaveTrack*>(&track);
track.TypeSwitch( [&](WaveTrack *mtw){
const double rate = mtw->GetRate();
// set it to a sample point
desiredSlideAmount = rint(desiredSlideAmount * rate) / rate;
}
});
// Adjust desiredSlideAmount using SnapManager
if (pSnapManager) {
@ -715,22 +713,27 @@ UIHandle::Result TimeShiftHandle::Drag
// If the mouse is over a track that isn't the captured track,
// decide which tracks the captured clips should go to.
if (mClipMoveState.capturedClip &&
pTrack != mCapturedTrack &&
pTrack->GetKind() == Track::Wave
/* && !mCapturedClipIsSelection*/)
{
if ( DoSlideVertical( viewInfo, event.m_x, mClipMoveState,
*trackList, *mCapturedTrack, *pTrack, desiredSlideAmount ) ) {
mCapturedTrack = pTrack;
mDidSlideVertically = true;
}
else
return RefreshAll;
bool fail = (
mClipMoveState.capturedClip &&
pTrack != mCapturedTrack
/* && !mCapturedClipIsSelection*/
&& pTrack->TypeSwitch<bool>( [&] (WaveTrack *) {
if ( DoSlideVertical( viewInfo, event.m_x, mClipMoveState,
*trackList, *mCapturedTrack, *pTrack, desiredSlideAmount ) ) {
mCapturedTrack = pTrack;
mDidSlideVertically = true;
}
else
return true;
// Not done yet, check for horizontal movement.
slidVertically = true;
}
// Not done yet, check for horizontal movement.
slidVertically = true;
return false;
})
);
if (fail)
return RefreshAll;
if (desiredSlideAmount == 0.0)
return RefreshAll;