diff --git a/src/Printing.cpp b/src/Printing.cpp index b14eb7393..481b470fa 100644 --- a/src/Printing.cpp +++ b/src/Printing.cpp @@ -72,7 +72,8 @@ bool AudacityPrintout::OnPrintPage(int WXUNUSED(page)) dc->GetSize(&width, &height); int rulerScreenHeight = 40; - int screenTotalHeight = mTracks->GetHeight() + rulerScreenHeight; + int screenTotalHeight = + TrackView::GetTotalHeight( *mTracks ) + rulerScreenHeight; double scale = height / (double)screenTotalHeight; diff --git a/src/ProjectWindow.cpp b/src/ProjectWindow.cpp index e0b944730..effa0cc8f 100644 --- a/src/ProjectWindow.cpp +++ b/src/ProjectWindow.cpp @@ -29,6 +29,7 @@ Paul Licameli split from AudacityProject.cpp #include "toolbars/ControlToolBar.h" #include "toolbars/ToolManager.h" #include "tracks/ui/Scrubbing.h" +#include "tracks/ui/TrackView.h" #include "widgets/wxPanelWrapper.h" #include "widgets/WindowAccessible.h" @@ -1137,7 +1138,7 @@ void ProjectWindow::FixScrollbars() bool refresh = false; bool rescroll = false; - int totalHeight = (tracks.GetHeight() + 32); + int totalHeight = TrackView::GetTotalHeight( tracks ) + 32; int panelWidth, panelHeight; trackPanel.GetTracksUsableArea(&panelWidth, &panelHeight); diff --git a/src/Track.cpp b/src/Track.cpp index 8d34889ec..9a4d50cd3 100644 --- a/src/Track.cpp +++ b/src/Track.cpp @@ -41,8 +41,6 @@ and TimeTrack. #include "InconsistencyException.h" -#include "tracks/ui/TrackView.h" - #ifdef _MSC_VER //Disable truncation warnings #pragma warning( disable : 4786 ) @@ -115,7 +113,7 @@ Track::Holder Track::Duplicate() const if (mpView) // Copy view state that might be important to undo/redo - TrackView::Get( *result ).Copy( *mpView ); + mpView->CopyTo( *result ); return result; } @@ -140,6 +138,26 @@ void Track::SetOwner mNode = node; } +const std::shared_ptr &Track::GetTrackView() +{ + return mpView; +} + +void Track::SetTrackView( const std::shared_ptr &pView ) +{ + mpView = pView; +} + +const std::shared_ptr &Track::GetTrackControls() +{ + return mpControls; +} + +void Track::SetTrackControls( const std::shared_ptr &pControls ) +{ + mpControls = pControls; +} + int Track::GetIndex() const { return mIndex; @@ -156,7 +174,6 @@ void Track::SetLinked(bool l) if (pList && !pList->mPendingUpdates.empty()) { auto orig = pList->FindById( GetId() ); if (orig && orig != this) { - // delegate, and rely on RecalcPositions to copy back orig->SetLinked(l); return; } @@ -532,23 +549,17 @@ void TrackList::RecalcPositions(TrackNodePointer node) Track *t; int i = 0; - int y = 0; auto prev = getPrev( node ); if ( !isNull( prev ) ) { t = prev.first->get(); i = t->GetIndex() + 1; - auto &view = TrackView::Get( *t ); - y = view.GetY() + view.GetHeight(); } const auto theEnd = end(); for (auto n = Find( node.first->get() ); n != theEnd; ++n) { t = *n; - auto &view = TrackView::Get( *t ); t->SetIndex(i++); - view.SetY(y); - y += view.GetHeight(); } UpdatePendingTracks(); @@ -568,16 +579,21 @@ void TrackList::DataEvent( const std::shared_ptr &pTrack, int code ) safenew TrackListEvent{ EVT_TRACKLIST_TRACK_DATA_CHANGE, pTrack, code } ); } -void TrackList::PermutationEvent() +void TrackList::PermutationEvent(TrackNodePointer node) { // wxWidgets will own the event object - QueueEvent( safenew TrackListEvent{ EVT_TRACKLIST_PERMUTED } ); + QueueEvent( safenew TrackListEvent{ EVT_TRACKLIST_PERMUTED, *node.first } ); } -void TrackList::DeletionEvent() +void TrackList::DeletionEvent(TrackNodePointer node) { // wxWidgets will own the event object - QueueEvent( safenew TrackListEvent{ EVT_TRACKLIST_DELETION } ); + QueueEvent( safenew TrackListEvent{ + EVT_TRACKLIST_DELETION, + node.second && node.first != node.second->end() + ? *node.first + : nullptr + } ); } void TrackList::AdditionEvent(TrackNodePointer node) @@ -631,7 +647,7 @@ void TrackList::Permute(const std::vector &permutation) } auto n = getBegin(); RecalcPositions(n); - PermutationEvent(); + PermutationEvent(n); } Track *TrackList::FindById( TrackId id ) @@ -738,7 +754,7 @@ auto TrackList::Replace(Track * t, const ListOfTracks::value_type &with) -> pTrack->SetId( t->GetId() ); RecalcPositions(node); - DeletionEvent(); + DeletionEvent(node); AdditionEvent(node); } return holder; @@ -759,7 +775,7 @@ TrackNodePointer TrackList::Remove(Track *t) if ( !isNull( result ) ) RecalcPositions(result); - DeletionEvent(); + DeletionEvent(result); } } return result; @@ -842,15 +858,6 @@ Track *TrackList::GetPrev(Track * t, bool linked) const return nullptr; } -/// For mono track height of track -/// For stereo track combined height of both channels. -int TrackList::GetGroupHeight(const Track * t) const -{ - const auto GetHeight = []( const Track *track ) - { return TrackView::Get( *track ).GetHeight(); }; - return Channels(t).sum( GetHeight ); -} - bool TrackList::CanMoveUp(Track * t) const { return GetPrev(t, true) != NULL; @@ -921,7 +928,7 @@ void TrackList::SwapNodes(TrackNodePointer s1, TrackNodePointer s2) // Now correct the Index in the tracks, and other things RecalcPositions(s1); - PermutationEvent(); + PermutationEvent(s1); } bool TrackList::MoveUp(Track * t) @@ -970,19 +977,6 @@ size_t TrackList::size() const return cnt; } -int TrackList::GetHeight() const -{ - int height = 0; - - if (!empty()) { - auto track = getPrev( getEnd() ).first->get(); - auto &view = TrackView::Get( *track ); - height = view.GetY() + view.GetHeight(); - } - - return height; -} - namespace { // Abstract the common pattern of the following three member functions inline double Accumulate @@ -1073,6 +1067,11 @@ void TrackList::ClearPendingTracks( ListOfTracks *pAdded ) if (pAdded) pAdded->clear(); + // To find the first node that remains after the first deleted one + TrackNodePointer node; + bool findingNode = false; + bool foundNode = false; + for (auto it = ListOfTracks::begin(), stop = ListOfTracks::end(); it != stop;) { if (it->get()->GetId() == TrackId{}) { @@ -1080,13 +1079,23 @@ void TrackList::ClearPendingTracks( ListOfTracks *pAdded ) pAdded->push_back( *it ); (*it)->SetOwner( {}, {} ); it = erase( it ); + + if (!findingNode) + findingNode = true; + if (!foundNode && it != stop) + node = (*it)->GetNode(); } - else + else { + if ( findingNode ) + foundNode = true; ++it; + } } - if (!empty()) + if (!empty()) { RecalcPositions(getBegin()); + DeletionEvent( node ); + } } bool TrackList::ApplyPendingTracks() @@ -1147,7 +1156,9 @@ bool TrackList::ApplyPendingTracks() } } if (inserted) { - RecalcPositions({first, this}); + TrackNodePointer node{first, this}; + RecalcPositions(node); + AdditionEvent(node); result = true; } @@ -1198,9 +1209,10 @@ void Track::WriteCommonXMLAttributes( xmlFile.WriteAttr(wxT("name"), GetName()); xmlFile.WriteAttr(wxT("isSelected"), this->GetSelected()); } - auto &view = TrackView::Get( *this ); - xmlFile.WriteAttr(wxT("height"), view.GetActualHeight()); - xmlFile.WriteAttr(wxT("minimized"), view.GetMinimized()); + if ( mpView ) + mpView->WriteXMLAttributes( xmlFile ); + if ( mpControls ) + mpControls->WriteXMLAttributes( xmlFile ); } // Return true iff the attribute is recognized. @@ -1208,21 +1220,15 @@ bool Track::HandleCommonXMLAttribute(const wxChar *attr, const wxChar *value) { long nValue = -1; wxString strValue( value ); - if (!wxStrcmp(attr, wxT("name")) && + if ( mpView && mpView->HandleXMLAttribute( attr, value ) ) + ; + else if ( mpControls && mpControls->HandleXMLAttribute( attr, value ) ) + ; + else if (!wxStrcmp(attr, wxT("name")) && XMLValueChecker::IsGoodString(strValue)) { SetName( strValue ); return true; } - else if (!wxStrcmp(attr, wxT("height")) && - XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue)) { - TrackView::Get( *this ).SetHeight(nValue); - return true; - } - else if (!wxStrcmp(attr, wxT("minimized")) && - XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue)) { - TrackView::Get( *this ).SetMinimized(nValue != 0); - return true; - } else if (!wxStrcmp(attr, wxT("isSelected")) && XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue)) { this->SetSelected(nValue != 0); @@ -1284,13 +1290,3 @@ void TrackFactory::Destroy( AudacityProject &project ) { project.AttachedObjects::Assign( key2, nullptr ); } - -template<> auto DoGetControls::Implementation() -> Function { - return nullptr; -} -static DoGetControls registerDoGetControls; - -template<> auto DoGetView::Implementation() -> Function { - return nullptr; -} -static DoGetView registerDoGetView; diff --git a/src/Track.h b/src/Track.h index 74b1bfba6..d850a983a 100644 --- a/src/Track.h +++ b/src/Track.h @@ -261,15 +261,15 @@ class AUDACITY_DLL_API Track /* not final */ public: mutable wxSize vrulerSize; - // Return another, associated TrackPanelCell object that implements - // click and drag and keystrokes in the track contents. - std::shared_ptr GetTrackView(); - std::shared_ptr GetTrackView() const; + // These are exposed only for the purposes of the TrackView class, to + // initialize the pointer on demand + const std::shared_ptr &GetTrackView(); + void SetTrackView( const std::shared_ptr &pView ); - // Return another, associated TrackPanelCell object that implements the - // drop-down, close and minimize buttons, etc. - std::shared_ptr GetTrackControls(); - std::shared_ptr GetTrackControls() const; + // These are exposed only for the purposes of the TrackControls class, to + // initialize the pointer on demand + const std::shared_ptr &GetTrackControls(); + void SetTrackControls( const std::shared_ptr &pControls ); // Return another, associated TrackPanelCell object that implements the @@ -704,7 +704,7 @@ public: bool HandleCommonXMLAttribute(const wxChar *attr, const wxChar *value); protected: - std::shared_ptr mpView; + std::shared_ptr mpView; std::shared_ptr mpControls; }; @@ -1092,6 +1092,7 @@ wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API, EVT_TRACKLIST_TRACK_DATA_CHANGE, TrackListEvent); // Posted when tracks are reordered but otherwise unchanged. +// mpTrack points to the moved track that is earliest in the New ordering. wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API, EVT_TRACKLIST_PERMUTED, TrackListEvent); @@ -1105,7 +1106,8 @@ wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API, EVT_TRACKLIST_ADDITION, TrackListEvent); // Posted when a track has been deleted from a tracklist. -// Also posted when one track replaces another +// Also posted when one track replaces another. +// mpTrack points to the first track after the deletion, if there is one. wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API, EVT_TRACKLIST_DELETION, TrackListEvent); @@ -1360,8 +1362,6 @@ public: /// Make the list empty void Clear(bool sendEvent = true); - int GetGroupHeight(const Track * t) const; - bool CanMoveUp(Track * t) const; bool CanMoveDown(Track * t) const; @@ -1393,7 +1393,6 @@ public: double GetEndTime() const; double GetMinOffset() const; - int GetHeight() const; #if LEGACY_PROJECT_FILE_SUPPORT // File I/O @@ -1490,9 +1489,9 @@ private: void RecalcPositions(TrackNodePointer node); void SelectionEvent( const std::shared_ptr &pTrack ); - void PermutationEvent(); + void PermutationEvent(TrackNodePointer node); void DataEvent( const std::shared_ptr &pTrack, int code ); - void DeletionEvent(); + void DeletionEvent(TrackNodePointer node = {}); void AdditionEvent(TrackNodePointer node); void ResizingEvent(TrackNodePointer node); @@ -1589,24 +1588,4 @@ class AUDACITY_DLL_API TrackFactory final #endif }; -#include "AttachedVirtualFunction.h" - -struct DoGetControlsTag; - -using DoGetControls = -AttachedVirtualFunction< - DoGetControlsTag, - std::shared_ptr< TrackControls >, - Track ->; - -struct DoGetViewTag; - -using DoGetView = -AttachedVirtualFunction< - DoGetViewTag, - std::shared_ptr< TrackView >, - Track ->; - #endif diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index 65b221f21..914cfa25b 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -832,10 +832,8 @@ void TrackPanel::RefreshTrack(Track *trk, bool refreshbacking) trk = *GetTracks()->FindLeader(trk); auto &view = TrackView::Get( *trk ); - const auto GetHeight = []( const Track *track ) - { return TrackView::Get( *track ).GetHeight(); }; auto height = - TrackList::Channels(trk).sum( GetHeight ) + TrackList::Channels(trk).sum( TrackView::GetTrackHeight ) - kTopInset - kShadowThickness; // subtract insets and shadows from the rectangle, but not border @@ -1327,9 +1325,7 @@ void TrackPanel::EnsureVisible(Track * t) trackTop += trackHeight; auto channels = TrackList::Channels(it); - const auto GetHeight = []( const Track *track ) - { return TrackView::Get( *track ).GetHeight(); }; - trackHeight = channels.sum( GetHeight ); + trackHeight = channels.sum( TrackView::GetTrackHeight ); //We have found the track we want to ensure is visible. if (channels.contains(t)) { @@ -1363,15 +1359,13 @@ void TrackPanel::VerticalScroll( float fracPosition){ int trackHeight = 0; auto tracks = GetTracks(); - const auto GetHeight = - [&]( const Track *t ){ return tracks->GetGroupHeight(t); }; auto range = tracks->Leaders(); if (!range.empty()) { - trackHeight = GetHeight( *range.rbegin() ); + trackHeight = TrackView::GetChannelGroupHeight( *range.rbegin() ); --range.second; } - trackTop = range.sum( GetHeight ); + trackTop = range.sum( TrackView::GetChannelGroupHeight ); int delta; diff --git a/src/commands/GetInfoCommand.cpp b/src/commands/GetInfoCommand.cpp index 36eaa5513..8ea5d3f8a 100644 --- a/src/commands/GetInfoCommand.cpp +++ b/src/commands/GetInfoCommand.cpp @@ -497,7 +497,7 @@ bool GetInfoCommand::SendTracks(const CommandContext & context) context.AddBool( (trk == fTrack), "focused"); context.AddBool( trk->GetSelected(), "selected" ); //JKC: Possibly add later... - //context.AddItem( GetTrackView::Get( *trk ).GetHeight(), "height" ); + //context.AddItem( TrackView::Get( *trk ).GetHeight(), "height" ); trk->TypeSwitch( [&] (const WaveTrack* t ) { float vzmin, vzmax; t->GetDisplayBounds(&vzmin, &vzmax); diff --git a/src/menus/ViewMenus.cpp b/src/menus/ViewMenus.cpp index 4c8e8e8a6..e311aaebd 100644 --- a/src/menus/ViewMenus.cpp +++ b/src/menus/ViewMenus.cpp @@ -206,10 +206,9 @@ void DoZoomFitV(AudacityProject &project) height -= 28; // The height of minimized and non-audio tracks cannot be apportioned - const auto GetHeight = []( const Track *track ) - { return TrackView::Get( *track ).GetHeight(); }; height -= - tracks.Any().sum( GetHeight ) - range.sum( GetHeight ); + tracks.Any().sum( TrackView::GetTrackHeight ) + - range.sum( TrackView::GetTrackHeight ); // Give each resized track the average of the remaining height height = height / count; diff --git a/src/tracks/labeltrack/ui/LabelTrackView.cpp b/src/tracks/labeltrack/ui/LabelTrackView.cpp index 620a22e87..12c75c127 100644 --- a/src/tracks/labeltrack/ui/LabelTrackView.cpp +++ b/src/tracks/labeltrack/ui/LabelTrackView.cpp @@ -97,13 +97,14 @@ void LabelTrackView::UnbindFrom( LabelTrack *pParent ) EVT_LABELTRACK_PERMUTED, &LabelTrackView::OnLabelPermuted, this ); } -void LabelTrackView::Copy( const TrackView &other ) +void LabelTrackView::CopyTo( Track &track ) const { - TrackView::Copy( other ); + TrackView::CopyTo( track ); + auto &other = TrackView::Get( track ); if ( const auto pOther = dynamic_cast< const LabelTrackView* >( &other ) ) { // only one field is important to preserve in undo/redo history - mSelIndex = pOther->mSelIndex; + pOther->mSelIndex = mSelIndex; } } diff --git a/src/tracks/labeltrack/ui/LabelTrackView.h b/src/tracks/labeltrack/ui/LabelTrackView.h index 4a25aabb7..39376d28d 100644 --- a/src/tracks/labeltrack/ui/LabelTrackView.h +++ b/src/tracks/labeltrack/ui/LabelTrackView.h @@ -82,7 +82,7 @@ private: std::shared_ptr DoGetVRulerControls() override; // Preserve some view state too for undo/redo purposes - void Copy( const TrackView &other ) override; + void CopyTo( Track &track ) const override; public: static void DoEditLabels( diff --git a/src/tracks/ui/CommonTrackPanelCell.cpp b/src/tracks/ui/CommonTrackPanelCell.cpp index 5c3caeb26..f34fafe78 100644 --- a/src/tracks/ui/CommonTrackPanelCell.cpp +++ b/src/tracks/ui/CommonTrackPanelCell.cpp @@ -57,6 +57,10 @@ CommonTrackCell::CommonTrackCell( const std::shared_ptr< Track > &parent ) CommonTrackCell::~CommonTrackCell() = default; +void CommonTrackCell::CopyTo( Track& ) const +{ +} + void CommonTrackCell::Reparent( const std::shared_ptr &parent ) { mwTrack = parent; @@ -66,3 +70,12 @@ std::shared_ptr CommonTrackCell::DoFindTrack() { return mwTrack.lock(); } + +void CommonTrackCell::WriteXMLAttributes( XMLWriter & ) const +{ +} + +bool CommonTrackCell::HandleXMLAttribute( const wxChar *, const wxChar * ) +{ + return false; +} diff --git a/src/tracks/ui/CommonTrackPanelCell.h b/src/tracks/ui/CommonTrackPanelCell.h index a571624b5..b229a4c60 100644 --- a/src/tracks/ui/CommonTrackPanelCell.h +++ b/src/tracks/ui/CommonTrackPanelCell.h @@ -19,6 +19,7 @@ Paul Licameli split from TrackPanel.cpp #include class Track; +class XMLWriter; class AUDACITY_DLL_API CommonTrackPanelCell /* not final */ : public TrackPanelCell @@ -61,10 +62,20 @@ public: ~CommonTrackCell(); + // Copy state, for undo/redo purposes + // The default does nothing + virtual void CopyTo( Track &track ) const; + std::shared_ptr DoFindTrack() override; virtual void Reparent( const std::shared_ptr &parent ); + // default does nothing + virtual void WriteXMLAttributes( XMLWriter & ) const; + + // default recognizes no attributes, and returns false + virtual bool HandleXMLAttribute( const wxChar *attr, const wxChar *value ); + private: std::weak_ptr< Track > mwTrack; }; diff --git a/src/tracks/ui/TrackControls.cpp b/src/tracks/ui/TrackControls.cpp index 0c850ec59..5e4dee257 100644 --- a/src/tracks/ui/TrackControls.cpp +++ b/src/tracks/ui/TrackControls.cpp @@ -24,11 +24,20 @@ TrackControls::~TrackControls() TrackControls &TrackControls::Get( Track &track ) { - return *static_cast( track.GetTrackControls().get() ); + auto pControls = + std::static_pointer_cast( track.GetTrackControls() ); + if (!pControls) + // create on demand + track.SetTrackControls( pControls = DoGetControls::Call( track ) ); + return *pControls; } const TrackControls &TrackControls::Get( const Track &track ) { - return *static_cast( track.GetTrackControls().get() ); + return Get( const_cast< Track& >( track ) ); } +template<> auto DoGetControls::Implementation() -> Function { + return nullptr; +} +static DoGetControls registerDoGetControls; diff --git a/src/tracks/ui/TrackControls.h b/src/tracks/ui/TrackControls.h index 317548343..11f451bc6 100644 --- a/src/tracks/ui/TrackControls.h +++ b/src/tracks/ui/TrackControls.h @@ -28,4 +28,15 @@ public: virtual ~TrackControls() = 0; }; +#include "AttachedVirtualFunction.h" + +struct DoGetControlsTag; + +using DoGetControls = +AttachedVirtualFunction< + DoGetControlsTag, + std::shared_ptr< TrackControls >, + Track +>; + #endif diff --git a/src/tracks/ui/TrackSelectHandle.cpp b/src/tracks/ui/TrackSelectHandle.cpp index 63738bc54..3761b3de9 100644 --- a/src/tracks/ui/TrackSelectHandle.cpp +++ b/src/tracks/ui/TrackSelectHandle.cpp @@ -11,6 +11,7 @@ Paul Licameli split from TrackPanel.cpp #include "../../Audacity.h" #include "TrackSelectHandle.h" +#include "TrackView.h" #include "../../Menus.h" #include "../../Project.h" #include "../../ProjectAudioIO.h" @@ -219,7 +220,7 @@ void TrackSelectHandle::CalculateRearrangingThresholds(const wxMouseEvent & even if (tracks.CanMoveUp(mpTrack.get())) mMoveUpThreshold = event.m_y - - tracks.GetGroupHeight( + TrackView::GetChannelGroupHeight( * -- tracks.FindLeader( mpTrack.get() ) ); else mMoveUpThreshold = INT_MIN; @@ -227,7 +228,7 @@ void TrackSelectHandle::CalculateRearrangingThresholds(const wxMouseEvent & even if (tracks.CanMoveDown(mpTrack.get())) mMoveDownThreshold = event.m_y + - tracks.GetGroupHeight( + TrackView::GetChannelGroupHeight( * ++ tracks.FindLeader( mpTrack.get() ) ); else mMoveDownThreshold = INT_MAX; diff --git a/src/tracks/ui/TrackView.cpp b/src/tracks/ui/TrackView.cpp index 843fdee4f..7d25afe14 100644 --- a/src/tracks/ui/TrackView.cpp +++ b/src/tracks/ui/TrackView.cpp @@ -14,27 +14,61 @@ Paul Licameli split from TrackPanel.cpp #include "TrackControls.h" #include "../../TrackPanelResizerCell.h" +#include "../../ClientData.h" +#include "../../Project.h" +#include "../../xml/XMLTagHandler.h" +#include "../../xml/XMLWriter.h" + TrackView::~TrackView() { } -void TrackView::Copy( const TrackView &other ) +int TrackView::GetTrackHeight( const Track *pTrack ) { - mMinimized = other.mMinimized; + return pTrack ? Get( *pTrack ).GetHeight() : 0; +} - // Let mY remain 0 -- TrackList::RecalcPositions corrects it later - mY = 0; - mHeight = other.mHeight; +int TrackView::GetChannelGroupHeight( const Track *pTrack ) +{ + return pTrack ? TrackList::Channels( pTrack ).sum( GetTrackHeight ) : 0; +} + +int TrackView::GetCumulativeHeight( const Track *pTrack ) +{ + if ( !pTrack ) + return 0; + auto &view = Get( *pTrack ); + return view.GetY() + view.GetHeight(); +} + +int TrackView::GetTotalHeight( const TrackList &list ) +{ + return GetCumulativeHeight( *list.Any().rbegin() ); +} + +void TrackView::CopyTo( Track &track ) const +{ + auto &other = Get( track ); + + other.mMinimized = mMinimized; + + // Let mY remain 0 -- TrackPositioner corrects it later + other.mY = 0; + other.mHeight = mHeight; } TrackView &TrackView::Get( Track &track ) { - return *track.GetTrackView(); + auto pView = std::static_pointer_cast( track.GetTrackView() ); + if (!pView) + // create on demand + track.SetTrackView( pView = DoGetView::Call( track ) ); + return *pView; } const TrackView &TrackView::Get( const Track &track ) { - return *track.GetTrackView(); + return Get( const_cast< Track& >( track ) ); } void TrackView::SetMinimized(bool isMinimized) @@ -48,37 +82,35 @@ void TrackView::SetMinimized(bool isMinimized) leader->AdjustPositions(); } +void TrackView::WriteXMLAttributes( XMLWriter &xmlFile ) const +{ + xmlFile.WriteAttr(wxT("height"), GetActualHeight()); + xmlFile.WriteAttr(wxT("minimized"), GetMinimized()); +} + +bool TrackView::HandleXMLAttribute( const wxChar *attr, const wxChar *value ) +{ + wxString strValue; + long nValue; + if (!wxStrcmp(attr, wxT("height")) && + XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue)) { + SetHeight(nValue); + return true; + } + else if (!wxStrcmp(attr, wxT("minimized")) && + XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue)) { + SetMinimized(nValue != 0); + return true; + } + else + return false; +} + void TrackView::DoSetMinimized(bool isMinimized) { mMinimized = isMinimized; } -std::shared_ptr Track::GetTrackView() -{ - if (!mpView) - // create on demand - mpView = DoGetView::Call( *this ); - return mpView; -} - -std::shared_ptr Track::GetTrackView() const -{ - return const_cast(this)->GetTrackView(); -} - -std::shared_ptr Track::GetTrackControls() -{ - if (!mpControls) - // create on demand - mpControls = DoGetControls::Call( *this ); - return mpControls; -} - -std::shared_ptr Track::GetTrackControls() const -{ - return const_cast< Track* >( this )->GetTrackControls(); -} - std::shared_ptr TrackView::GetVRulerControls() { if (!mpVRulerControls) @@ -129,3 +161,58 @@ void TrackView::DoSetHeight(int h) { mHeight = h; } + +namespace { + +// Attach an object to each project. It receives track list events and updates +// track Y coordinates +struct TrackPositioner : ClientData::Base, wxEvtHandler +{ + AudacityProject &mProject; + + explicit TrackPositioner( AudacityProject &project ) + : mProject{ project } + { + TrackList::Get( project ).Bind( + EVT_TRACKLIST_ADDITION, &TrackPositioner::OnUpdate, this ); + TrackList::Get( project ).Bind( + EVT_TRACKLIST_DELETION, &TrackPositioner::OnUpdate, this ); + TrackList::Get( project ).Bind( + EVT_TRACKLIST_PERMUTED, &TrackPositioner::OnUpdate, this ); + TrackList::Get( project ).Bind( + EVT_TRACKLIST_RESIZING, &TrackPositioner::OnUpdate, this ); + } + + void OnUpdate( TrackListEvent & e ) + { + e.Skip(); + + auto iter = + TrackList::Get( mProject ).Find( e.mpTrack.lock().get() ); + if ( !*iter ) + return; + + auto prev = iter; + auto yy = TrackView::GetCumulativeHeight( *--prev ); + + while( auto pTrack = *iter ) { + auto &view = TrackView::Get( *pTrack ); + view.SetY( yy ); + yy += view.GetHeight(); + ++iter; + } + } +}; + +static const AudacityProject::AttachedObjects::RegisteredFactory key{ + []( AudacityProject &project ){ + return std::make_shared< TrackPositioner >( project ); + } +}; + +} + +template<> auto DoGetView::Implementation() -> Function { + return nullptr; +} +static DoGetView registerDoGetView; diff --git a/src/tracks/ui/TrackView.h b/src/tracks/ui/TrackView.h index 5fa4a564b..fcf855b15 100644 --- a/src/tracks/ui/TrackView.h +++ b/src/tracks/ui/TrackView.h @@ -15,6 +15,7 @@ Paul Licameli split from class Track #include "CommonTrackPanelCell.h" // to inherit class Track; +class TrackList; class TrackVRulerControls; class TrackPanelResizerCell; @@ -32,8 +33,16 @@ public: : CommonTrackCell{ pTrack } {} virtual ~TrackView() = 0; + // some static conveniences, useful for summation over track iterator + // ranges + static int GetTrackHeight( const Track *pTrack ); + static int GetChannelGroupHeight( const Track *pTrack ); + // Total height of the given track and all previous ones (constant time!) + static int GetCumulativeHeight( const Track *pTrack ); + static int GetTotalHeight( const TrackList &list ); + // Copy view state, for undo/redo purposes - virtual void Copy( const TrackView &other ); + void CopyTo( Track &track ) const override; static TrackView &Get( Track & ); static const TrackView &Get( const Track & ); @@ -60,6 +69,9 @@ public: std::shared_ptr GetResizer(); std::shared_ptr GetResizer() const; + void WriteXMLAttributes( XMLWriter & ) const override; + bool HandleXMLAttribute( const wxChar *attr, const wxChar *value ) override; + protected: virtual void DoSetMinimized( bool isMinimized ); @@ -81,4 +93,15 @@ private: int mHeight{ DefaultHeight }; }; +#include "AttachedVirtualFunction.h" + +struct DoGetViewTag; + +using DoGetView = +AttachedVirtualFunction< + DoGetViewTag, + std::shared_ptr< TrackView >, + Track +>; + #endif