mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-15 15:49:36 +02:00
Move TrackShifter subclasses each to its own new source file...
... These do not require header files; they are tiny proto-plugins that work by registration of AttachedVirtualFunction overrides at startup. Dependency may go from them to TrackView subclasses but not back; so in principle a reduced Audacity without time-shift could be linked that still uses the TrackView subclasses. But other work to make the hit test for the Time Shift tool registrable would have to precede that.
This commit is contained in:
parent
4abd38b9a0
commit
2b542bf734
@ -774,6 +774,7 @@ list( APPEND SOURCES
|
||||
tracks/labeltrack/ui/LabelTextHandle.h
|
||||
tracks/labeltrack/ui/LabelTrackControls.cpp
|
||||
tracks/labeltrack/ui/LabelTrackControls.h
|
||||
tracks/labeltrack/ui/LabelTrackShifter.cpp
|
||||
tracks/labeltrack/ui/LabelTrackVRulerControls.cpp
|
||||
tracks/labeltrack/ui/LabelTrackVRulerControls.h
|
||||
tracks/labeltrack/ui/LabelTrackView.cpp
|
||||
@ -782,6 +783,7 @@ list( APPEND SOURCES
|
||||
tracks/playabletrack/notetrack/ui/NoteTrackButtonHandle.h
|
||||
tracks/playabletrack/notetrack/ui/NoteTrackControls.cpp
|
||||
tracks/playabletrack/notetrack/ui/NoteTrackControls.h
|
||||
tracks/playabletrack/notetrack/ui/NoteTrackShifter.cpp
|
||||
tracks/playabletrack/notetrack/ui/NoteTrackSliderHandles.cpp
|
||||
tracks/playabletrack/notetrack/ui/NoteTrackSliderHandles.h
|
||||
tracks/playabletrack/notetrack/ui/NoteTrackVRulerControls.cpp
|
||||
@ -808,6 +810,7 @@ list( APPEND SOURCES
|
||||
tracks/playabletrack/wavetrack/ui/SpectrumView.h
|
||||
tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp
|
||||
tracks/playabletrack/wavetrack/ui/WaveTrackControls.h
|
||||
tracks/playabletrack/wavetrack/ui/WaveTrackShifter.cpp
|
||||
tracks/playabletrack/wavetrack/ui/WaveTrackSliderHandles.cpp
|
||||
tracks/playabletrack/wavetrack/ui/WaveTrackSliderHandles.h
|
||||
tracks/playabletrack/wavetrack/ui/WaveTrackVRulerControls.cpp
|
||||
|
256
src/tracks/labeltrack/ui/LabelTrackShifter.cpp
Normal file
256
src/tracks/labeltrack/ui/LabelTrackShifter.cpp
Normal file
@ -0,0 +1,256 @@
|
||||
/*!
|
||||
@file LabelTrackShifter.cpp
|
||||
@brief headerless file injects method definitions for time shifting of LabelTrack
|
||||
*/
|
||||
|
||||
#include "LabelTrackView.h"
|
||||
#include "../../ui/TimeShiftHandle.h"
|
||||
#include "../../../LabelTrack.h"
|
||||
#include "../../../ViewInfo.h"
|
||||
|
||||
class LabelTrackShifter final : public TrackShifter {
|
||||
public:
|
||||
LabelTrackShifter( LabelTrack &track, AudacityProject &project )
|
||||
: mpTrack{ track.SharedPointer<LabelTrack>() }
|
||||
, mProject{ project }
|
||||
{
|
||||
InitIntervals();
|
||||
mpTrack->Bind(
|
||||
EVT_LABELTRACK_PERMUTED, &LabelTrackShifter::OnLabelPermuted, this );
|
||||
mpTrack->Bind(
|
||||
EVT_LABELTRACK_ADDITION, &LabelTrackShifter::OnLabelAdded, this );
|
||||
mpTrack->Bind(
|
||||
EVT_LABELTRACK_DELETION, &LabelTrackShifter::OnLabelDeleted, this );
|
||||
}
|
||||
~LabelTrackShifter() override
|
||||
{
|
||||
mpTrack->Unbind(
|
||||
EVT_LABELTRACK_PERMUTED, &LabelTrackShifter::OnLabelPermuted, this );
|
||||
mpTrack->Unbind(
|
||||
EVT_LABELTRACK_ADDITION, &LabelTrackShifter::OnLabelAdded, this );
|
||||
mpTrack->Unbind(
|
||||
EVT_LABELTRACK_DELETION, &LabelTrackShifter::OnLabelDeleted, this );
|
||||
}
|
||||
Track &GetTrack() const override { return *mpTrack; }
|
||||
|
||||
static inline size_t& GetIndex(TrackInterval &interval)
|
||||
{
|
||||
auto pExtra =
|
||||
static_cast<LabelTrack::IntervalData*>( interval.Extra() );
|
||||
return pExtra->index;
|
||||
}
|
||||
|
||||
static inline size_t GetIndex(const TrackInterval &interval)
|
||||
{
|
||||
return GetIndex( const_cast<TrackInterval&>(interval) );
|
||||
}
|
||||
|
||||
HitTestResult HitTest(
|
||||
double time, const ViewInfo &viewInfo, HitTestParams *pParams ) override
|
||||
{
|
||||
HitTestResult result = HitTestResult::Intervals;
|
||||
auto t0 = viewInfo.selectedRegion.t0();
|
||||
auto t1 = viewInfo.selectedRegion.t1();
|
||||
if ( mpTrack->IsSelected() && time >= t0 && time < t1 )
|
||||
result = HitTestResult::Selection;
|
||||
|
||||
// Prefer the box that the mouse hovers over, else the selected one
|
||||
int iLabel = -1;
|
||||
if ( pParams )
|
||||
iLabel =
|
||||
LabelTrackView::OverATextBox(*mpTrack, pParams->xx, pParams->yy);
|
||||
if (iLabel == -1)
|
||||
iLabel = LabelTrackView::Get(*mpTrack).GetSelectedIndex(mProject);
|
||||
if (iLabel != -1) {
|
||||
UnfixIntervals([&](const auto &myInterval){
|
||||
return GetIndex( myInterval ) == iLabel;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
// If the pick is within the selection, which overlaps some intervals,
|
||||
// then move those intervals only
|
||||
// Else move all labels (preserving the older beahvior of time shift)
|
||||
if ( result == HitTestResult::Selection )
|
||||
SelectInterval({ t0, t1 });
|
||||
if (mMoving.empty())
|
||||
return HitTestResult::Track;
|
||||
else
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void SelectInterval( const TrackInterval &interval ) override
|
||||
{
|
||||
CommonSelectInterval(interval);
|
||||
}
|
||||
|
||||
bool SyncLocks() override { return false; }
|
||||
|
||||
bool MayMigrateTo( Track &otherTrack ) override
|
||||
{
|
||||
return CommonMayMigrateTo(otherTrack);
|
||||
}
|
||||
|
||||
/* We need to copy a complete label when detaching it because LabelStruct
|
||||
is stored in a vector in LabelTrack without an extra indirection.
|
||||
So the detached intervals handed back to the caller are unlike those
|
||||
reported by LabelTrack, but carry the extra information. */
|
||||
struct IntervalData final: Track::IntervalData {
|
||||
SelectedRegion region;
|
||||
wxString title;
|
||||
IntervalData(const LabelStruct &label)
|
||||
: region{label.selectedRegion}
|
||||
, title{label.title}
|
||||
{}
|
||||
};
|
||||
|
||||
Intervals Detach() override
|
||||
{
|
||||
auto pTrack = mpTrack.get();
|
||||
auto moveLabel = [pTrack](TrackInterval &interval) -> TrackInterval {
|
||||
auto &rindex = GetIndex(interval);
|
||||
auto index = rindex;
|
||||
rindex = -1;
|
||||
auto result = TrackInterval{
|
||||
interval.Start(), interval.End(),
|
||||
std::make_unique<IntervalData>( *pTrack->GetLabel(index) ) };
|
||||
pTrack->DeleteLabel(index);
|
||||
return result;
|
||||
};
|
||||
Intervals result;
|
||||
std::transform(
|
||||
// Reverse traversal may lessen the shifting-left in the label array
|
||||
mMoving.rbegin(), mMoving.rend(), std::back_inserter(result),
|
||||
moveLabel );
|
||||
mMoving = Intervals{};
|
||||
return result;
|
||||
}
|
||||
|
||||
bool AdjustFit(
|
||||
const Track &, const Intervals &, double &, double ) override
|
||||
{
|
||||
// Labels have no overlapping constraints, so just...
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Attach( Intervals intervals ) override
|
||||
{
|
||||
auto pTrack = mpTrack.get();
|
||||
std::for_each( intervals.rbegin(), intervals.rend(),
|
||||
[this, pTrack](auto &interval){
|
||||
auto pData = static_cast<IntervalData*>( interval.Extra() );
|
||||
auto index = pTrack->AddLabel(pData->region, pData->title);
|
||||
// Recreate the simpler TrackInterval as would be reported by LabelTrack
|
||||
mMoving.emplace_back( pTrack->MakeInterval(index) );
|
||||
} );
|
||||
return true;
|
||||
}
|
||||
|
||||
void DoHorizontalOffset( double offset ) override
|
||||
{
|
||||
auto &labels = mpTrack->GetLabels();
|
||||
for ( auto &interval : MovingIntervals() ) {
|
||||
auto index = GetIndex( interval );
|
||||
auto labelStruct = labels[index];
|
||||
labelStruct.selectedRegion.move(offset);
|
||||
mpTrack->SetLabel( index, labelStruct );
|
||||
}
|
||||
|
||||
mpTrack->SortLabels(); // causes callback to OnLabelPermuted
|
||||
}
|
||||
|
||||
private:
|
||||
void OnLabelPermuted( LabelTrackEvent &e )
|
||||
{
|
||||
e.Skip();
|
||||
if ( e.mpTrack.lock() != mpTrack )
|
||||
return;
|
||||
|
||||
auto former = e.mFormerPosition;
|
||||
auto present = e.mPresentPosition;
|
||||
|
||||
// Avoid signed-unsigned comparison below!
|
||||
if (former < 0 || present < 0) {
|
||||
wxASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
auto update = [=]( TrackInterval &interval ){
|
||||
auto &index = GetIndex( interval );
|
||||
if ( index == former )
|
||||
index = present;
|
||||
else if ( former < index && index <= present )
|
||||
-- index;
|
||||
else if ( former > index && index >= present )
|
||||
++ index;
|
||||
};
|
||||
|
||||
std::for_each(mFixed.begin(), mFixed.end(), update);
|
||||
std::for_each(mMoving.begin(), mMoving.end(), update);
|
||||
}
|
||||
|
||||
void OnLabelAdded( LabelTrackEvent &e )
|
||||
{
|
||||
e.Skip();
|
||||
if ( e.mpTrack.lock() != mpTrack )
|
||||
return;
|
||||
|
||||
auto present = e.mPresentPosition;
|
||||
|
||||
// Avoid signed-unsigned comparison below!
|
||||
if (present < 0) {
|
||||
wxASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
auto update = [=]( TrackInterval &interval ){
|
||||
auto pExtra = static_cast<LabelTrack::IntervalData*>(interval.Extra());
|
||||
auto &index = pExtra->index;
|
||||
if ( index >= present )
|
||||
++ index;
|
||||
};
|
||||
|
||||
std::for_each(mFixed.begin(), mFixed.end(), update);
|
||||
std::for_each(mMoving.begin(), mMoving.end(), update);
|
||||
}
|
||||
|
||||
void OnLabelDeleted( LabelTrackEvent &e )
|
||||
{
|
||||
e.Skip();
|
||||
if ( e.mpTrack.lock() != mpTrack )
|
||||
return;
|
||||
|
||||
auto former = e.mFormerPosition;
|
||||
|
||||
// Avoid signed-unsigned comparison below!
|
||||
if (former < 0) {
|
||||
wxASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
auto update = [=]( TrackInterval &interval ){
|
||||
auto pExtra = static_cast<LabelTrack::IntervalData*>(interval.Extra());
|
||||
auto &index = pExtra->index;
|
||||
if ( index > former )
|
||||
-- index;
|
||||
else if ( index == former )
|
||||
// It should have been deleted first!
|
||||
wxASSERT( false );
|
||||
};
|
||||
|
||||
std::for_each(mFixed.begin(), mFixed.end(), update);
|
||||
std::for_each(mMoving.begin(), mMoving.end(), update);
|
||||
}
|
||||
|
||||
std::shared_ptr<LabelTrack> mpTrack;
|
||||
AudacityProject &mProject;
|
||||
};
|
||||
|
||||
using MakeLabelTrackShifter = MakeTrackShifter::Override<LabelTrack>;
|
||||
template<> template<> auto MakeLabelTrackShifter::Implementation() -> Function {
|
||||
return [](LabelTrack &track, AudacityProject &project) {
|
||||
return std::make_unique<LabelTrackShifter>(track, project);
|
||||
};
|
||||
}
|
||||
static MakeLabelTrackShifter registerMakeLabelTrackShifter;
|
@ -2133,252 +2133,3 @@ std::shared_ptr<TrackVRulerControls> LabelTrackView::DoGetVRulerControls()
|
||||
return
|
||||
std::make_shared<LabelTrackVRulerControls>( shared_from_this() );
|
||||
}
|
||||
|
||||
#include "../../ui/TimeShiftHandle.h"
|
||||
|
||||
class LabelTrackShifter final : public TrackShifter {
|
||||
public:
|
||||
LabelTrackShifter( LabelTrack &track, AudacityProject &project )
|
||||
: mpTrack{ track.SharedPointer<LabelTrack>() }
|
||||
, mProject{ project }
|
||||
{
|
||||
InitIntervals();
|
||||
mpTrack->Bind(
|
||||
EVT_LABELTRACK_PERMUTED, &LabelTrackShifter::OnLabelPermuted, this );
|
||||
mpTrack->Bind(
|
||||
EVT_LABELTRACK_ADDITION, &LabelTrackShifter::OnLabelAdded, this );
|
||||
mpTrack->Bind(
|
||||
EVT_LABELTRACK_DELETION, &LabelTrackShifter::OnLabelDeleted, this );
|
||||
}
|
||||
~LabelTrackShifter() override
|
||||
{
|
||||
mpTrack->Unbind(
|
||||
EVT_LABELTRACK_PERMUTED, &LabelTrackShifter::OnLabelPermuted, this );
|
||||
mpTrack->Unbind(
|
||||
EVT_LABELTRACK_ADDITION, &LabelTrackShifter::OnLabelAdded, this );
|
||||
mpTrack->Unbind(
|
||||
EVT_LABELTRACK_DELETION, &LabelTrackShifter::OnLabelDeleted, this );
|
||||
}
|
||||
Track &GetTrack() const override { return *mpTrack; }
|
||||
|
||||
static inline size_t& GetIndex(TrackInterval &interval)
|
||||
{
|
||||
auto pExtra =
|
||||
static_cast<LabelTrack::IntervalData*>( interval.Extra() );
|
||||
return pExtra->index;
|
||||
}
|
||||
|
||||
static inline size_t GetIndex(const TrackInterval &interval)
|
||||
{
|
||||
return GetIndex( const_cast<TrackInterval&>(interval) );
|
||||
}
|
||||
|
||||
HitTestResult HitTest(
|
||||
double time, const ViewInfo &viewInfo, HitTestParams *pParams ) override
|
||||
{
|
||||
HitTestResult result = HitTestResult::Intervals;
|
||||
auto t0 = viewInfo.selectedRegion.t0();
|
||||
auto t1 = viewInfo.selectedRegion.t1();
|
||||
if ( mpTrack->IsSelected() && time >= t0 && time < t1 )
|
||||
result = HitTestResult::Selection;
|
||||
|
||||
// Prefer the box that the mouse hovers over, else the selected one
|
||||
int iLabel = -1;
|
||||
if ( pParams )
|
||||
iLabel =
|
||||
LabelTrackView::OverATextBox(*mpTrack, pParams->xx, pParams->yy);
|
||||
if (iLabel == -1)
|
||||
iLabel = LabelTrackView::Get(*mpTrack).GetSelectedIndex(mProject);
|
||||
if (iLabel != -1) {
|
||||
UnfixIntervals([&](const auto &myInterval){
|
||||
return GetIndex( myInterval ) == iLabel;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
// If the pick is within the selection, which overlaps some intervals,
|
||||
// then move those intervals only
|
||||
// Else move all labels (preserving the older beahvior of time shift)
|
||||
if ( result == HitTestResult::Selection )
|
||||
SelectInterval({ t0, t1 });
|
||||
if (mMoving.empty())
|
||||
return HitTestResult::Track;
|
||||
else
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void SelectInterval( const TrackInterval &interval ) override
|
||||
{
|
||||
CommonSelectInterval(interval);
|
||||
}
|
||||
|
||||
bool SyncLocks() override { return false; }
|
||||
|
||||
bool MayMigrateTo( Track &otherTrack ) override
|
||||
{
|
||||
return CommonMayMigrateTo(otherTrack);
|
||||
}
|
||||
|
||||
/* We need to copy a complete label when detaching it because LabelStruct
|
||||
is stored in a vector in LabelTrack without an extra indirection.
|
||||
So the detached intervals handed back to the caller are unlike those
|
||||
reported by LabelTrack, but carry the extra information. */
|
||||
struct IntervalData final: Track::IntervalData {
|
||||
SelectedRegion region;
|
||||
wxString title;
|
||||
IntervalData(const LabelStruct &label)
|
||||
: region{label.selectedRegion}
|
||||
, title{label.title}
|
||||
{}
|
||||
};
|
||||
|
||||
Intervals Detach() override
|
||||
{
|
||||
auto pTrack = mpTrack.get();
|
||||
auto moveLabel = [pTrack](TrackInterval &interval) -> TrackInterval {
|
||||
auto &rindex = GetIndex(interval);
|
||||
auto index = rindex;
|
||||
rindex = -1;
|
||||
auto result = TrackInterval{
|
||||
interval.Start(), interval.End(),
|
||||
std::make_unique<IntervalData>( *pTrack->GetLabel(index) ) };
|
||||
pTrack->DeleteLabel(index);
|
||||
return result;
|
||||
};
|
||||
Intervals result;
|
||||
std::transform(
|
||||
// Reverse traversal may lessen the shifting-left in the label array
|
||||
mMoving.rbegin(), mMoving.rend(), std::back_inserter(result),
|
||||
moveLabel );
|
||||
mMoving = Intervals{};
|
||||
return result;
|
||||
}
|
||||
|
||||
bool AdjustFit(
|
||||
const Track &, const Intervals &, double &, double ) override
|
||||
{
|
||||
// Labels have no overlapping constraints, so just...
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Attach( Intervals intervals ) override
|
||||
{
|
||||
auto pTrack = mpTrack.get();
|
||||
std::for_each( intervals.rbegin(), intervals.rend(),
|
||||
[this, pTrack](auto &interval){
|
||||
auto pData = static_cast<IntervalData*>( interval.Extra() );
|
||||
auto index = pTrack->AddLabel(pData->region, pData->title);
|
||||
// Recreate the simpler TrackInterval as would be reported by LabelTrack
|
||||
mMoving.emplace_back( pTrack->MakeInterval(index) );
|
||||
} );
|
||||
return true;
|
||||
}
|
||||
|
||||
void DoHorizontalOffset( double offset ) override
|
||||
{
|
||||
auto &labels = mpTrack->GetLabels();
|
||||
for ( auto &interval : MovingIntervals() ) {
|
||||
auto index = GetIndex( interval );
|
||||
auto labelStruct = labels[index];
|
||||
labelStruct.selectedRegion.move(offset);
|
||||
mpTrack->SetLabel( index, labelStruct );
|
||||
}
|
||||
|
||||
mpTrack->SortLabels(); // causes callback to OnLabelPermuted
|
||||
}
|
||||
|
||||
private:
|
||||
void OnLabelPermuted( LabelTrackEvent &e )
|
||||
{
|
||||
e.Skip();
|
||||
if ( e.mpTrack.lock() != mpTrack )
|
||||
return;
|
||||
|
||||
auto former = e.mFormerPosition;
|
||||
auto present = e.mPresentPosition;
|
||||
|
||||
// Avoid signed-unsigned comparison below!
|
||||
if (former < 0 || present < 0) {
|
||||
wxASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
auto update = [=]( TrackInterval &interval ){
|
||||
auto &index = GetIndex( interval );
|
||||
if ( index == former )
|
||||
index = present;
|
||||
else if ( former < index && index <= present )
|
||||
-- index;
|
||||
else if ( former > index && index >= present )
|
||||
++ index;
|
||||
};
|
||||
|
||||
std::for_each(mFixed.begin(), mFixed.end(), update);
|
||||
std::for_each(mMoving.begin(), mMoving.end(), update);
|
||||
}
|
||||
|
||||
void OnLabelAdded( LabelTrackEvent &e )
|
||||
{
|
||||
e.Skip();
|
||||
if ( e.mpTrack.lock() != mpTrack )
|
||||
return;
|
||||
|
||||
auto present = e.mPresentPosition;
|
||||
|
||||
// Avoid signed-unsigned comparison below!
|
||||
if (present < 0) {
|
||||
wxASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
auto update = [=]( TrackInterval &interval ){
|
||||
auto pExtra = static_cast<LabelTrack::IntervalData*>(interval.Extra());
|
||||
auto &index = pExtra->index;
|
||||
if ( index >= present )
|
||||
++ index;
|
||||
};
|
||||
|
||||
std::for_each(mFixed.begin(), mFixed.end(), update);
|
||||
std::for_each(mMoving.begin(), mMoving.end(), update);
|
||||
}
|
||||
|
||||
void OnLabelDeleted( LabelTrackEvent &e )
|
||||
{
|
||||
e.Skip();
|
||||
if ( e.mpTrack.lock() != mpTrack )
|
||||
return;
|
||||
|
||||
auto former = e.mFormerPosition;
|
||||
|
||||
// Avoid signed-unsigned comparison below!
|
||||
if (former < 0) {
|
||||
wxASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
auto update = [=]( TrackInterval &interval ){
|
||||
auto pExtra = static_cast<LabelTrack::IntervalData*>(interval.Extra());
|
||||
auto &index = pExtra->index;
|
||||
if ( index > former )
|
||||
-- index;
|
||||
else if ( index == former )
|
||||
// It should have been deleted first!
|
||||
wxASSERT( false );
|
||||
};
|
||||
|
||||
std::for_each(mFixed.begin(), mFixed.end(), update);
|
||||
std::for_each(mMoving.begin(), mMoving.end(), update);
|
||||
}
|
||||
|
||||
std::shared_ptr<LabelTrack> mpTrack;
|
||||
AudacityProject &mProject;
|
||||
};
|
||||
|
||||
using MakeLabelTrackShifter = MakeTrackShifter::Override<LabelTrack>;
|
||||
template<> template<> auto MakeLabelTrackShifter::Implementation() -> Function {
|
||||
return [](LabelTrack &track, AudacityProject &project) {
|
||||
return std::make_unique<LabelTrackShifter>(track, project);
|
||||
};
|
||||
}
|
||||
static MakeLabelTrackShifter registerMakeLabelTrackShifter;
|
||||
|
49
src/tracks/playabletrack/notetrack/ui/NoteTrackShifter.cpp
Normal file
49
src/tracks/playabletrack/notetrack/ui/NoteTrackShifter.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
/*!
|
||||
@file NoteTrackShifter.cpp
|
||||
@brief headerless file injects method definitions for time shifting of NoteTrack
|
||||
*/
|
||||
|
||||
#include "../../../ui/TimeShiftHandle.h"
|
||||
#include "../../../../NoteTrack.h"
|
||||
#include "../../../../ViewInfo.h"
|
||||
|
||||
class NoteTrackShifter final : public TrackShifter {
|
||||
public:
|
||||
NoteTrackShifter( NoteTrack &track )
|
||||
: mpTrack{ track.SharedPointer<NoteTrack>() }
|
||||
{
|
||||
InitIntervals();
|
||||
}
|
||||
~NoteTrackShifter() override {}
|
||||
Track &GetTrack() const override { return *mpTrack; }
|
||||
|
||||
HitTestResult HitTest(
|
||||
double time, const ViewInfo &viewInfo, HitTestParams* ) override
|
||||
{
|
||||
UnfixAll();
|
||||
auto t0 = viewInfo.selectedRegion.t0();
|
||||
auto t1 = viewInfo.selectedRegion.t1();
|
||||
if ( mpTrack->IsSelected() && time >= t0 && time < t1 )
|
||||
return HitTestResult::Selection;
|
||||
else
|
||||
return HitTestResult::Intervals;
|
||||
}
|
||||
|
||||
void SelectInterval( const TrackInterval &interval ) override
|
||||
{
|
||||
CommonSelectInterval( interval );
|
||||
}
|
||||
|
||||
bool SyncLocks() override { return true; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<NoteTrack> mpTrack;
|
||||
};
|
||||
|
||||
using MakeNoteTrackShifter = MakeTrackShifter::Override<NoteTrack>;
|
||||
template<> template<> auto MakeNoteTrackShifter::Implementation() -> Function {
|
||||
return [](NoteTrack &track, AudacityProject&) {
|
||||
return std::make_unique<NoteTrackShifter>(track);
|
||||
};
|
||||
}
|
||||
static MakeNoteTrackShifter registerMakeNoteTrackShifter;
|
@ -729,48 +729,4 @@ void NoteTrackView::Draw(
|
||||
}
|
||||
CommonTrackView::Draw( context, rect, iPass );
|
||||
}
|
||||
|
||||
#include "../../../ui/TimeShiftHandle.h"
|
||||
|
||||
class NoteTrackShifter final : public TrackShifter {
|
||||
public:
|
||||
NoteTrackShifter( NoteTrack &track )
|
||||
: mpTrack{ track.SharedPointer<NoteTrack>() }
|
||||
{
|
||||
InitIntervals();
|
||||
}
|
||||
~NoteTrackShifter() override {}
|
||||
Track &GetTrack() const override { return *mpTrack; }
|
||||
|
||||
HitTestResult HitTest(
|
||||
double time, const ViewInfo &viewInfo, HitTestParams* ) override
|
||||
{
|
||||
UnfixAll();
|
||||
auto t0 = viewInfo.selectedRegion.t0();
|
||||
auto t1 = viewInfo.selectedRegion.t1();
|
||||
if ( mpTrack->IsSelected() && time >= t0 && time < t1 )
|
||||
return HitTestResult::Selection;
|
||||
else
|
||||
return HitTestResult::Intervals;
|
||||
}
|
||||
|
||||
void SelectInterval( const TrackInterval &interval ) override
|
||||
{
|
||||
CommonSelectInterval( interval );
|
||||
}
|
||||
|
||||
bool SyncLocks() override { return true; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<NoteTrack> mpTrack;
|
||||
};
|
||||
|
||||
using MakeNoteTrackShifter = MakeTrackShifter::Override<NoteTrack>;
|
||||
template<> template<> auto MakeNoteTrackShifter::Implementation() -> Function {
|
||||
return [](NoteTrack &track, AudacityProject&) {
|
||||
return std::make_unique<NoteTrackShifter>(track);
|
||||
};
|
||||
}
|
||||
static MakeNoteTrackShifter registerMakeNoteTrackShifter;
|
||||
|
||||
#endif
|
||||
|
178
src/tracks/playabletrack/wavetrack/ui/WaveTrackShifter.cpp
Normal file
178
src/tracks/playabletrack/wavetrack/ui/WaveTrackShifter.cpp
Normal file
@ -0,0 +1,178 @@
|
||||
/*!
|
||||
@file WaveTrackShifter.cpp
|
||||
@brief headerless file injects method definitions for time shifting of WaveTrack
|
||||
*/
|
||||
|
||||
#include "../../../ui/TimeShiftHandle.h"
|
||||
#include "../../../../ViewInfo.h"
|
||||
#include "../../../../WaveClip.h"
|
||||
#include "../../../../WaveTrack.h"
|
||||
|
||||
class WaveTrackShifter final : public TrackShifter {
|
||||
public:
|
||||
WaveTrackShifter( WaveTrack &track )
|
||||
: mpTrack{ track.SharedPointer<WaveTrack>() }
|
||||
{
|
||||
InitIntervals();
|
||||
}
|
||||
~WaveTrackShifter() override {}
|
||||
Track &GetTrack() const override { return *mpTrack; }
|
||||
|
||||
HitTestResult HitTest(
|
||||
double time, const ViewInfo &viewInfo, HitTestParams* ) override
|
||||
{
|
||||
auto pClip = mpTrack->GetClipAtTime( time );
|
||||
|
||||
if (!pClip)
|
||||
return HitTestResult::Miss;
|
||||
|
||||
auto t0 = viewInfo.selectedRegion.t0();
|
||||
auto t1 = viewInfo.selectedRegion.t1();
|
||||
if ( mpTrack->IsSelected() && time >= t0 && time < t1 ) {
|
||||
// Unfix maybe many intervals (at least one because of test above)
|
||||
SelectInterval({t0, t1});
|
||||
return HitTestResult::Selection;
|
||||
}
|
||||
|
||||
// Select just one interval
|
||||
UnfixIntervals( [&](const auto &interval){
|
||||
return
|
||||
static_cast<WaveTrack::IntervalData*>(interval.Extra())
|
||||
->GetClip().get() == pClip;
|
||||
} );
|
||||
|
||||
return HitTestResult::Intervals;
|
||||
}
|
||||
|
||||
void SelectInterval( const TrackInterval &interval ) override
|
||||
{
|
||||
UnfixIntervals( [&](auto &myInterval){
|
||||
// Use a slightly different test from CommonSelectInterval, rounding times
|
||||
// to exact samples according to the clip's rate
|
||||
auto data =
|
||||
static_cast<WaveTrack::IntervalData*>( myInterval.Extra() );
|
||||
auto clip = data->GetClip().get();
|
||||
return !(clip->IsClipStartAfterClip(interval.Start()) ||
|
||||
clip->BeforeClip(interval.End()));
|
||||
});
|
||||
}
|
||||
|
||||
bool SyncLocks() override { return true; }
|
||||
|
||||
bool MayMigrateTo(Track &other) override
|
||||
{
|
||||
return TrackShifter::CommonMayMigrateTo(other);
|
||||
}
|
||||
|
||||
double HintOffsetLarger(double desiredOffset) override
|
||||
{
|
||||
// set it to a sample point, and minimum of 1 sample point
|
||||
bool positive = (desiredOffset > 0);
|
||||
if (!positive)
|
||||
desiredOffset *= -1;
|
||||
double nSamples = rint(mpTrack->GetRate() * desiredOffset);
|
||||
nSamples = std::max(nSamples, 1.0);
|
||||
desiredOffset = nSamples / mpTrack->GetRate();
|
||||
if (!positive)
|
||||
desiredOffset *= -1;
|
||||
return desiredOffset;
|
||||
}
|
||||
|
||||
double QuantizeOffset( double desiredOffset ) override
|
||||
{
|
||||
const auto rate = mpTrack->GetRate();
|
||||
// set it to a sample point
|
||||
return rint(desiredOffset * rate) / rate;
|
||||
}
|
||||
|
||||
double AdjustOffsetSmaller(double desiredOffset) override
|
||||
{
|
||||
std::vector< WaveClip * > movingClips;
|
||||
for ( auto &interval : MovingIntervals() ) {
|
||||
auto data =
|
||||
static_cast<WaveTrack::IntervalData*>( interval.Extra() );
|
||||
movingClips.push_back(data->GetClip().get());
|
||||
}
|
||||
double newAmount = 0;
|
||||
(void) mpTrack->CanOffsetClips(movingClips, desiredOffset, &newAmount);
|
||||
return newAmount;
|
||||
}
|
||||
|
||||
Intervals Detach() override
|
||||
{
|
||||
for ( auto &interval: mMoving ) {
|
||||
auto pData = static_cast<WaveTrack::IntervalData*>( interval.Extra() );
|
||||
auto pClip = pData->GetClip().get();
|
||||
// interval will still hold the clip, so ignore the return:
|
||||
(void) mpTrack->RemoveAndReturnClip(pClip);
|
||||
mMigrated.erase(pClip);
|
||||
}
|
||||
return std::move( mMoving );
|
||||
}
|
||||
|
||||
bool AdjustFit(
|
||||
const Track &otherTrack, const Intervals &intervals,
|
||||
double &desiredOffset, double tolerance) override
|
||||
{
|
||||
bool ok = true;
|
||||
auto pOtherWaveTrack = static_cast<const WaveTrack*>(&otherTrack);
|
||||
for ( auto &interval: intervals ) {
|
||||
auto pData =
|
||||
static_cast<WaveTrack::IntervalData*>( interval.Extra() );
|
||||
auto pClip = pData->GetClip().get();
|
||||
ok = pOtherWaveTrack->CanInsertClip(
|
||||
pClip, desiredOffset, tolerance );
|
||||
if( !ok )
|
||||
break;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Attach( Intervals intervals ) override
|
||||
{
|
||||
for (auto &interval : intervals) {
|
||||
auto pData = static_cast<WaveTrack::IntervalData*>( interval.Extra() );
|
||||
auto pClip = pData->GetClip();
|
||||
if ( !mpTrack->AddClip( pClip ) )
|
||||
return false;
|
||||
mMigrated.insert( pClip.get() );
|
||||
mMoving.emplace_back( std::move( interval ) );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FinishMigration() override
|
||||
{
|
||||
auto rate = mpTrack->GetRate();
|
||||
for (auto pClip : mMigrated) {
|
||||
// Now that user has dropped the clip into a different track,
|
||||
// make sure the sample rate matches the destination track.
|
||||
pClip->Resample(rate);
|
||||
pClip->MarkChanged();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DoHorizontalOffset( double offset ) override
|
||||
{
|
||||
for ( auto &interval : MovingIntervals() ) {
|
||||
auto data =
|
||||
static_cast<WaveTrack::IntervalData*>( interval.Extra() );
|
||||
data->GetClip()->Offset( offset );
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<WaveTrack> mpTrack;
|
||||
|
||||
// Clips that may require resampling
|
||||
std::unordered_set<WaveClip *> mMigrated;
|
||||
};
|
||||
|
||||
using MakeWaveTrackShifter = MakeTrackShifter::Override<WaveTrack>;
|
||||
template<> template<> auto MakeWaveTrackShifter::Implementation() -> Function {
|
||||
return [](WaveTrack &track, AudacityProject&) {
|
||||
return std::make_unique<WaveTrackShifter>(track);
|
||||
};
|
||||
}
|
||||
static MakeWaveTrackShifter registerMakeWaveTrackShifter;
|
@ -1303,172 +1303,3 @@ void WaveTrackView::Draw(
|
||||
|
||||
CommonTrackView::Draw( context, rect, iPass );
|
||||
}
|
||||
|
||||
class WaveTrackShifter final : public TrackShifter {
|
||||
public:
|
||||
WaveTrackShifter( WaveTrack &track )
|
||||
: mpTrack{ track.SharedPointer<WaveTrack>() }
|
||||
{
|
||||
InitIntervals();
|
||||
}
|
||||
~WaveTrackShifter() override {}
|
||||
Track &GetTrack() const override { return *mpTrack; }
|
||||
|
||||
HitTestResult HitTest(
|
||||
double time, const ViewInfo &viewInfo, HitTestParams* ) override
|
||||
{
|
||||
auto pClip = mpTrack->GetClipAtTime( time );
|
||||
|
||||
if (!pClip)
|
||||
return HitTestResult::Miss;
|
||||
|
||||
auto t0 = viewInfo.selectedRegion.t0();
|
||||
auto t1 = viewInfo.selectedRegion.t1();
|
||||
if ( mpTrack->IsSelected() && time >= t0 && time < t1 ) {
|
||||
// Unfix maybe many intervals (at least one because of test above)
|
||||
SelectInterval({t0, t1});
|
||||
return HitTestResult::Selection;
|
||||
}
|
||||
|
||||
// Select just one interval
|
||||
UnfixIntervals( [&](const auto &interval){
|
||||
return
|
||||
static_cast<WaveTrack::IntervalData*>(interval.Extra())
|
||||
->GetClip().get() == pClip;
|
||||
} );
|
||||
|
||||
return HitTestResult::Intervals;
|
||||
}
|
||||
|
||||
void SelectInterval( const TrackInterval &interval ) override
|
||||
{
|
||||
UnfixIntervals( [&](auto &myInterval){
|
||||
// Use a slightly different test from CommonSelectInterval, rounding times
|
||||
// to exact samples according to the clip's rate
|
||||
auto data =
|
||||
static_cast<WaveTrack::IntervalData*>( myInterval.Extra() );
|
||||
auto clip = data->GetClip().get();
|
||||
return !(clip->IsClipStartAfterClip(interval.Start()) ||
|
||||
clip->BeforeClip(interval.End()));
|
||||
});
|
||||
}
|
||||
|
||||
bool SyncLocks() override { return true; }
|
||||
|
||||
bool MayMigrateTo(Track &other) override
|
||||
{
|
||||
return TrackShifter::CommonMayMigrateTo(other);
|
||||
}
|
||||
|
||||
double HintOffsetLarger(double desiredOffset) override
|
||||
{
|
||||
// set it to a sample point, and minimum of 1 sample point
|
||||
bool positive = (desiredOffset > 0);
|
||||
if (!positive)
|
||||
desiredOffset *= -1;
|
||||
double nSamples = rint(mpTrack->GetRate() * desiredOffset);
|
||||
nSamples = std::max(nSamples, 1.0);
|
||||
desiredOffset = nSamples / mpTrack->GetRate();
|
||||
if (!positive)
|
||||
desiredOffset *= -1;
|
||||
return desiredOffset;
|
||||
}
|
||||
|
||||
double QuantizeOffset( double desiredOffset ) override
|
||||
{
|
||||
const auto rate = mpTrack->GetRate();
|
||||
// set it to a sample point
|
||||
return rint(desiredOffset * rate) / rate;
|
||||
}
|
||||
|
||||
double AdjustOffsetSmaller(double desiredOffset) override
|
||||
{
|
||||
std::vector< WaveClip * > movingClips;
|
||||
for ( auto &interval : MovingIntervals() ) {
|
||||
auto data =
|
||||
static_cast<WaveTrack::IntervalData*>( interval.Extra() );
|
||||
movingClips.push_back(data->GetClip().get());
|
||||
}
|
||||
double newAmount = 0;
|
||||
(void) mpTrack->CanOffsetClips(movingClips, desiredOffset, &newAmount);
|
||||
return newAmount;
|
||||
}
|
||||
|
||||
Intervals Detach() override
|
||||
{
|
||||
for ( auto &interval: mMoving ) {
|
||||
auto pData = static_cast<WaveTrack::IntervalData*>( interval.Extra() );
|
||||
auto pClip = pData->GetClip().get();
|
||||
// interval will still hold the clip, so ignore the return:
|
||||
(void) mpTrack->RemoveAndReturnClip(pClip);
|
||||
mMigrated.erase(pClip);
|
||||
}
|
||||
return std::move( mMoving );
|
||||
}
|
||||
|
||||
bool AdjustFit(
|
||||
const Track &otherTrack, const Intervals &intervals,
|
||||
double &desiredOffset, double tolerance) override
|
||||
{
|
||||
bool ok = true;
|
||||
auto pOtherWaveTrack = static_cast<const WaveTrack*>(&otherTrack);
|
||||
for ( auto &interval: intervals ) {
|
||||
auto pData =
|
||||
static_cast<WaveTrack::IntervalData*>( interval.Extra() );
|
||||
auto pClip = pData->GetClip().get();
|
||||
ok = pOtherWaveTrack->CanInsertClip(
|
||||
pClip, desiredOffset, tolerance );
|
||||
if( !ok )
|
||||
break;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Attach( Intervals intervals ) override
|
||||
{
|
||||
for (auto &interval : intervals) {
|
||||
auto pData = static_cast<WaveTrack::IntervalData*>( interval.Extra() );
|
||||
auto pClip = pData->GetClip();
|
||||
if ( !mpTrack->AddClip( pClip ) )
|
||||
return false;
|
||||
mMigrated.insert( pClip.get() );
|
||||
mMoving.emplace_back( std::move( interval ) );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FinishMigration() override
|
||||
{
|
||||
auto rate = mpTrack->GetRate();
|
||||
for (auto pClip : mMigrated) {
|
||||
// Now that user has dropped the clip into a different track,
|
||||
// make sure the sample rate matches the destination track.
|
||||
pClip->Resample(rate);
|
||||
pClip->MarkChanged();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DoHorizontalOffset( double offset ) override
|
||||
{
|
||||
for ( auto &interval : MovingIntervals() ) {
|
||||
auto data =
|
||||
static_cast<WaveTrack::IntervalData*>( interval.Extra() );
|
||||
data->GetClip()->Offset( offset );
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<WaveTrack> mpTrack;
|
||||
|
||||
// Clips that may require resampling
|
||||
std::unordered_set<WaveClip *> mMigrated;
|
||||
};
|
||||
|
||||
using MakeWaveTrackShifter = MakeTrackShifter::Override<WaveTrack>;
|
||||
template<> template<> auto MakeWaveTrackShifter::Implementation() -> Function {
|
||||
return [](WaveTrack &track, AudacityProject&) {
|
||||
return std::make_unique<WaveTrackShifter>(track);
|
||||
};
|
||||
}
|
||||
static MakeWaveTrackShifter registerMakeWaveTrackShifter;
|
||||
|
Loading…
x
Reference in New Issue
Block a user