Audacity 3.2.0
TrackPanelAx.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 TrackPanelAx.cpp
6
7 Leland Lucius
8 and lots of other contributors
9
10******************************************************************//*******************************************************************/
16
17
18#include "TrackPanelAx.h"
19
20// For compilers that support precompilation, includes "wx/wx.h".
21#include <wx/wxprec.h>
22
23#include <wx/setup.h> // for wxUSE_* macros
24
25#ifndef WX_PRECOMP
26// Include your minimal set of headers here, or wx.h
27#include <wx/wx.h>
28#endif
29
30#include "BasicUI.h"
31#include "Project.h"
32#include "SyncLock.h"
33#include "Track.h"
34#include "LabelTrack.h"
35#include "NoteTrack.h"
36#include "TimeTrack.h"
37
39 :
40#if wxUSE_ACCESSIBILITY
41 WindowAccessible( nullptr ) // window pointer must be set after construction
42 ,
43#endif
44 mProject{ project }
45{
46 mTrackName = true;
47 mMessageCount = 0;
49}
50
52{
53}
54
56{
57 return TrackList::Get( mProject );
58}
59
60// Returns currently focused track
61// if that track no longer exists, if there is a track at
62// the same position, use that, else if there is a first
63// track, use that.
64std::shared_ptr<Track> TrackPanelAx::GetFocus()
65{
66 auto focusedTrack = mFocusedTrack.lock();
67 if( !focusedTrack ) {
68 if (mNumFocusedTrack >=1) {
69 // This prevents the focus from being unnecessarily set to track 1
70 // when effects are applied. (Applying an effect can change
71 // the pointers of the selected tracks.)
72 focusedTrack = FindTrack(mNumFocusedTrack);
73 }
74 if (!focusedTrack) {
75 focusedTrack =
76 Track::SharedPointer(*GetTracks().Any().first);
77 // only call SetFocus if the focus has changed to avoid
78 // unnecessary focus events
79 if (focusedTrack)
80 focusedTrack = SetFocus();
81 }
82 }
83
84 if( !TrackNum( focusedTrack ) )
85 {
86 mFocusedTrack.reset();
87 return {};
88 }
89
90 return( focusedTrack );
91}
92
93// Changes focus to a specified track
94std::shared_ptr<Track> TrackPanelAx::SetFocus( std::shared_ptr<Track> track )
95{
96 mTrackName = true;
97
98#if wxUSE_ACCESSIBILITY
99
100 auto focusedTrack = mFocusedTrack.lock();
101 if( focusedTrack && !focusedTrack->GetSelected() )
102 {
103 NotifyEvent( wxACC_EVENT_OBJECT_SELECTIONREMOVE,
104 GetWindow(),
105 wxOBJID_CLIENT,
106 TrackNum( focusedTrack ) );
107 }
108#endif
109
110 if (!track)
112
113 if ( mFocusedTrack.lock() != track ) {
114 mFocusedTrack = track;
115 BasicUI::CallAfter([wFocus = TrackFocus::Get(mProject).weak_from_this()]{
116 if (auto pFocus = wFocus.lock())
117 pFocus->Publish({});
118 });
119 }
120 mNumFocusedTrack = TrackNum(track);
121
122#if wxUSE_ACCESSIBILITY
123 if( track )
124 {
126 {
127 NotifyEvent( wxACC_EVENT_OBJECT_FOCUS,
128 GetWindow(),
129 wxOBJID_CLIENT,
131 }
132
133 if( track->GetSelected() )
134 {
135 NotifyEvent( wxACC_EVENT_OBJECT_SELECTION,
136 GetWindow(),
137 wxOBJID_CLIENT,
139 }
140 }
141 else
142 {
143 NotifyEvent(wxACC_EVENT_OBJECT_FOCUS,
144 GetWindow(),
145 wxOBJID_CLIENT,
146 wxACC_SELF);
147 }
148
149#endif
150
151 return track;
152}
153
154int TrackPanelAx::TrackNum( const std::shared_ptr<Track> &target )
155{
156 // Find 1-based position of the target in the visible tracks, or 0 if not
157 // found
158 int ndx = 0;
159
160 for (auto t : GetTracks()) {
161 ndx++;
162 if( t == target.get() )
163 {
164 return ndx;
165 }
166 }
167
168 return 0;
169}
170
171std::shared_ptr<Track> TrackPanelAx::FindTrack( int num )
172{
173 int ndx = 0;
174
175 for (auto t : GetTracks()) {
176 ndx++;
177 if( ndx == num )
178 return t->SharedPointer();
179 }
180
181 return {};
182}
183
185{
186#if wxUSE_ACCESSIBILITY
187 auto t = GetFocus();
188 mTrackName = true;
189
190 // The object_focus event is only needed by Window-Eyes
191 // and can be removed when we cease to support this screen reader.
192 NotifyEvent(wxACC_EVENT_OBJECT_FOCUS,
193 GetWindow(),
194 wxOBJID_CLIENT,
195 TrackNum(t));
196
197 NotifyEvent(wxACC_EVENT_OBJECT_NAMECHANGE,
198 GetWindow(),
199 wxOBJID_CLIENT,
200 TrackNum(t));
201#endif
202}
203
205{
206#if wxUSE_ACCESSIBILITY
208 {
209 auto t = GetFocus();
210 int childId = t ? TrackNum(t) : 0;
211
212 mMessage = message.Translation();
213
214 // append \a alternatively, so that the string is never the same as the previous string.
215 // This ensures that screen readers read it.
216 if (mMessageCount % 2 == 0)
217 mMessage.Append('\a');
219
220 mTrackName = false;
221 NotifyEvent(wxACC_EVENT_OBJECT_NAMECHANGE,
222 GetWindow(),
223 wxOBJID_CLIENT,
224 childId);
225 }
226
227#endif
228}
229
230#if wxUSE_ACCESSIBILITY
231
232// Retrieves the address of an IDispatch interface for the specified child.
233// All objects must support this property.
234wxAccStatus TrackPanelAx::GetChild( int childId, wxAccessible** child )
235{
236 if( childId == wxACC_SELF )
237 {
238 *child = this;
239 }
240 else
241 {
242 *child = NULL;
243 }
244
245 return wxACC_OK;
246}
247
248// Gets the number of children.
249wxAccStatus TrackPanelAx::GetChildCount( int* childCount )
250{
251 *childCount = GetTracks().Any().size();
252 return wxACC_OK;
253}
254
255// Gets the default action for this object (0) or > 0 (the action for a child).
256// Return wxACC_OK even if there is no action. actionName is the action, or the empty
257// string if there is no action.
258// The retrieved string describes the action that is performed on an object,
259// not what the object does as a result. For example, a toolbar button that prints
260// a document has a default action of "Press" rather than "Prints the current document."
261wxAccStatus TrackPanelAx::GetDefaultAction( int WXUNUSED(childId), wxString *actionName )
262{
263 actionName->clear();
264
265 return wxACC_OK;
266}
267
268// Returns the description for this object or a child.
269wxAccStatus TrackPanelAx::GetDescription( int WXUNUSED(childId), wxString *description )
270{
271 description->clear();
272
273 return wxACC_OK;
274}
275
276// Returns help text for this object or a child, similar to tooltip text.
277wxAccStatus TrackPanelAx::GetHelpText( int WXUNUSED(childId), wxString *helpText )
278{
279 helpText->clear();
280
281 return wxACC_OK;
282}
283
284// Returns the keyboard shortcut for this object or child.
285// Return e.g. ALT+K
286wxAccStatus TrackPanelAx::GetKeyboardShortcut( int WXUNUSED(childId), wxString *shortcut )
287{
288 shortcut->clear();
289
290 return wxACC_OK;
291}
292
293// Returns the rectangle for this object (id = 0) or a child element (id > 0).
294// rect is in screen coordinates.
295wxAccStatus TrackPanelAx::GetLocation( wxRect& rect, int elementId )
296{
297 wxRect client;
298
299 if( elementId == wxACC_SELF )
300 {
301 rect = GetWindow()->GetScreenRect();
302 }
303 else
304 {
305 auto t = FindTrack( elementId );
306
307 if( t == NULL )
308 {
309 return wxACC_FAIL;
310 }
311
312 rect = mFinder ? mFinder( *t ) : wxRect{};
313 // Inflate the screen reader's rectangle so it overpaints Audacity's own
314 // yellow focus rectangle.
315#ifdef __WXMAC__
316 const int dx = 2;
317#else
318 const int dx = 1;
319#endif
320 rect.Inflate(dx, dx);
321 rect.SetPosition(GetWindow()->ClientToScreen(rect.GetPosition()));
322 }
323
324 return wxACC_OK;
325}
326
327// Gets the name of the specified object.
328wxAccStatus TrackPanelAx::GetName( int childId, wxString* name )
329{
330#if defined(__WXMSW__) || defined(__WXMAC__)
331 if (mTrackName)
332 {
333 if( childId == wxACC_SELF )
334 {
335 *name = _("TrackView");
336 }
337 else
338 {
339 auto t = FindTrack( childId );
340
341 if( t == NULL )
342 return wxACC_FAIL;
343
344 name->Printf("%d %s", TrackNum(t), t->GetName());
345
346 if(dynamic_cast<LabelTrack*>(t.get()))
347 {
348 const auto trackNameLower = t->GetName().Lower();
349 //Prior to version 3.2 "Label Track" was the default
350 //name for label tracks, don't append type part to the
351 //text to avoid verbosity.
352 if(trackNameLower.Find(wxString(_("Label Track")).Lower()) == wxNOT_FOUND &&
353 trackNameLower.Find(LabelTrack::GetDefaultName().Lower()) == wxNOT_FOUND)
354 /* i18n-hint: This is for screen reader software and indicates that
355 this is a Label track.*/
356 name->Append( wxT(" ") + wxString(_("Label Track")));
357 }
358 else if(dynamic_cast<TimeTrack*>(t.get()))
359 {
360 if(t->GetName().Lower().Find(TimeTrack::GetDefaultName().Lower()) == wxNOT_FOUND)
361 /* i18n-hint: This is for screen reader software and indicates that
362 this is a Time track.*/
363 name->Append(wxT(" ") + wxString(_("Time Track")));
364 }
365#ifdef USE_MIDI
366 else if(dynamic_cast<NoteTrack*>(t.get()))
367 /* i18n-hint: This is for screen reader software and indicates that
368 this is a Note track.*/
369 name->Append( wxT(" ") + wxString(_("Note Track")));
370#endif
371
372 // LLL: Remove these during "refactor"
373 auto pt = dynamic_cast<PlayableTrack *>(t.get());
374 if( pt && pt->GetMute() )
375 {
376 // The following comment also applies to the solo, selected,
377 // and synclockselected states.
378 // Many of translations of the strings with a leading space omitted
379 // the leading space. Therefore a space has been added using wxT(" ").
380 // Because screen readers won't be affected by multiple spaces, the
381 // leading spaces have not been removed, so that no NEW translations are needed.
382 /* i18n-hint: This is for screen reader software and indicates that
383 this track is muted. (The mute button is on.)*/
384 name->Append( wxT(" ") + wxString(_( " Muted" )) );
385 }
386
387 if( pt && pt->GetSolo() )
388 {
389 /* i18n-hint: This is for screen reader software and indicates that
390 this track is soloed. (The Solo button is on.)*/
391 name->Append( wxT(" ") + wxString(_( " Soloed" )) );
392 }
393 if( t->GetSelected() )
394 {
395 /* i18n-hint: This is for screen reader software and indicates that
396 this track is selected.*/
397 name->Append( wxT(" ") + wxString(_( " Selected" )) );
398 }
399 if (SyncLock::IsSyncLockSelected(t.get()))
400 {
401 /* i18n-hint: This is for screen reader software and indicates that
402 this track is shown with a sync-locked icon.*/
403 // The absence of a dash between Sync and Locked is deliberate -
404 // if present, Jaws reads it as "dash".
405 name->Append( wxT(" ") + wxString(_( " Sync Locked" )) );
406 }
407 }
408 }
409 else
410 {
411 *name = mMessage;
412 }
413
414 return wxACC_OK;
415#endif
416
417#if defined(__WXMAC__)
418 return wxACC_NOT_IMPLEMENTED;
419#endif
420}
421
422// Returns a role constant.
423wxAccStatus TrackPanelAx::GetRole( int childId, wxAccRole* role )
424{
425#if defined(__WXMSW__)
426 if (mTrackName)
427 {
428 if( childId == wxACC_SELF )
429 {
430 *role = wxROLE_SYSTEM_TABLE;
431 }
432 else
433 {
434 *role = wxROLE_SYSTEM_ROW;
435 }
436 }
437 else
438 {
439 *role = wxROLE_NONE;
440 }
441#endif
442
443#if defined(__WXMAC__)
444 if( childId == wxACC_SELF )
445 {
446 *role = wxROLE_SYSTEM_PANE;
447 }
448 else
449 {
450 *role = wxROLE_SYSTEM_STATICTEXT;
451 }
452#endif
453
454 return wxACC_OK;
455}
456
457// Gets a variant representing the selected children
458// of this object.
459// Acceptable values:
460// - a null variant (IsNull() returns TRUE)
461// - a list variant (GetType() == wxT("list"))
462// - an integer representing the selected child element,
463// or 0 if this object is selected (GetType() == wxT("long"))
464// - a "void*" pointer to a wxAccessible child object
465wxAccStatus TrackPanelAx::GetSelections( wxVariant * WXUNUSED(selections) )
466{
467 return wxACC_NOT_IMPLEMENTED;
468}
469
470// Returns a state constant.
471wxAccStatus TrackPanelAx::GetState( int childId, long* state )
472{
473#if defined(__WXMSW__)
474 if( childId > 0 )
475 {
476 auto t = FindTrack( childId );
477
478 *state = wxACC_STATE_SYSTEM_FOCUSABLE | wxACC_STATE_SYSTEM_SELECTABLE;
479 if (t)
480 {
481 if( t == mFocusedTrack.lock() )
482 {
483 *state |= wxACC_STATE_SYSTEM_FOCUSED;
484 }
485
486 if( t->GetSelected() && mTrackName )
487 {
488 *state |= wxACC_STATE_SYSTEM_SELECTED;
489 }
490 }
491 }
492 else // childId == wxACC_SELF
493 {
494 *state = wxACC_STATE_SYSTEM_FOCUSABLE + wxACC_STATE_SYSTEM_FOCUSED;
495 }
496#endif
497
498#if defined(__WXMAC__)
499 *state = wxACC_STATE_SYSTEM_FOCUSABLE | wxACC_STATE_SYSTEM_SELECTABLE;
500
501 if( childId > 0 )
502 {
503 auto t = FindTrack( childId );
504
505 if (t)
506 {
507 if( t == mFocusedTrack.lock() )
508 {
509 *state |= wxACC_STATE_SYSTEM_FOCUSED;
510 }
511
512 if( t->GetSelected() )
513 {
514 *state |= wxACC_STATE_SYSTEM_SELECTED;
515 }
516 }
517 }
518#endif
519
520 return wxACC_OK;
521}
522
523// Returns a localized string representing the value for the object
524// or child.
525#if defined(__WXMAC__)
526wxAccStatus TrackPanelAx::GetValue( int childId, wxString* strValue )
527#else
528wxAccStatus TrackPanelAx::GetValue( int WXUNUSED(childId), wxString* WXUNUSED(strValue) )
529#endif
530{
531#if defined(__WXMSW__)
532 return wxACC_NOT_IMPLEMENTED;
533#endif
534
535#if defined(__WXMAC__)
536 if( childId == wxACC_SELF )
537 {
538 *strValue = _("TrackView");
539 }
540 else
541 {
542 auto t = FindTrack( childId );
543
544 if( t == NULL )
545 {
546 return wxACC_FAIL;
547 }
548 else
549 {
550 /* i18n-hint: The %d is replaced by the number of the track.*/
551 strValue->Printf(_("Track %d"), TrackNum(t));
552 strValue->Append(" " + t->GetName());
553
554 // LLL: Remove these during "refactor"
555 auto pt = dynamic_cast<PlayableTrack *>(t.get());
556 if( pt && pt->GetMute() )
557 {
558 strValue->Append( _( " Mute On" ) );
559 }
560
561 if( pt && pt->GetSolo() )
562 {
563 strValue->Append( _( " Solo On" ) );
564 }
565 if( t->GetSelected() )
566 {
567 strValue->Append( _( " Select On" ) );
568 }
569 }
570 }
571 return wxACC_OK;
572#endif
573}
574
575// Gets the window with the keyboard focus.
576// If childId is 0 and child is NULL, no object in
577// this subhierarchy has the focus.
578// If this object has the focus, child should be 'this'.
579wxAccStatus TrackPanelAx::GetFocus( int *childId, wxAccessible **child )
580{
581#if defined(__WXMSW__)
582
584 {
585 auto focusedTrack = mFocusedTrack.lock();
586 if (focusedTrack)
587 {
588 *childId = TrackNum(focusedTrack);
589 }
590 else
591 {
592 *child = this;
593 }
594 }
595
596 return wxACC_OK;
597#endif
598
599#if defined(__WXMAC__)
600 if( GetWindow() == wxWindow::FindFocus() )
601 {
602 auto focusedTrack = mFocusedTrack.lock();
603 if( focusedTrack )
604 {
605 *childId = TrackNum( focusedTrack );
606 }
607 else
608 {
609 *childId = wxACC_SELF;
610 }
611
612 return wxACC_OK;
613 }
614
615 return wxACC_NOT_IMPLEMENTED;
616#endif
617}
618
619// Navigates from fromId to toId/toObject
620wxAccStatus TrackPanelAx::Navigate(wxNavDir navDir, int fromId, int* toId, wxAccessible** toObject)
621{
622 int childCount;
623 GetChildCount( &childCount );
624
625 if (fromId > childCount)
626 return wxACC_FAIL;
627
628 switch (navDir) {
629 case wxNAVDIR_FIRSTCHILD:
630 if (fromId == wxACC_SELF && childCount > 0 )
631 *toId = 1;
632 else
633 return wxACC_FALSE;
634 break;
635
636 case wxNAVDIR_LASTCHILD:
637 if (fromId == wxACC_SELF && childCount > 0 )
638 *toId = childCount;
639 else
640 return wxACC_FALSE;
641 break;
642
643 case wxNAVDIR_NEXT:
644 case wxNAVDIR_DOWN:
645 if (fromId != wxACC_SELF) {
646 *toId = fromId + 1;
647 if (*toId > childCount)
648 return wxACC_FALSE;
649 }
650 else
651 return wxACC_NOT_IMPLEMENTED;
652 break;
653
654 case wxNAVDIR_PREVIOUS:
655 case wxNAVDIR_UP:
656 if (fromId != wxACC_SELF) {
657 *toId = fromId - 1;
658 if (*toId < 1)
659 return wxACC_FALSE;
660 }
661 else
662 return wxACC_NOT_IMPLEMENTED;
663 break;
664
665 case wxNAVDIR_LEFT:
666 case wxNAVDIR_RIGHT:
667 if (fromId != wxACC_SELF)
668 return wxACC_FALSE;
669 else
670 return wxACC_NOT_IMPLEMENTED;
671 break;
672 }
673
674 *toObject = nullptr;
675 return wxACC_OK;
676}
677
678// Modify focus or selection
679wxAccStatus TrackPanelAx::Select(int childId, wxAccSelectionFlags selectFlags)
680{
681 // Only support change of focus
682 if (selectFlags != wxACC_SEL_TAKEFOCUS)
683 return wxACC_NOT_IMPLEMENTED;
684
685 if (childId != wxACC_SELF) {
686 int childCount;
687 GetChildCount( &childCount );
688 if (childId > childCount)
689 return wxACC_FAIL;
690
691 Track* t = FindTrack(childId).get();
692 if (t) {
693 SetFocus( t->SharedPointer() );
694 t->EnsureVisible();
695 }
696 }
697 else
698 return wxACC_NOT_IMPLEMENTED;
699
700 return wxACC_OK;
701}
702
703#endif // wxUSE_ACCESSIBILITY
704
706 []( AudacityProject &parent ){
707 return std::make_shared< TrackFocus >( parent );
708 }
709};
710
712{
713 return project.AttachedObjects::Get< TrackFocus >( key );
714}
715
717{
718 return Get( const_cast< AudacityProject & >( project ) );
719}
720
722 : mProject{ project }
723{
724}
725
727{
728}
729
731{
732 if (mAx)
733 return mAx->GetFocus().get();
734 return nullptr;
735}
736
737void TrackFocus::Set( Track *pTrack )
738{
739 if (mAx) {
740 pTrack = *TrackList::Get( mProject ).Find( pTrack );
741 mAx->SetFocus( Track::SharedPointer( pTrack ) );
742 }
743}
744
746 wxWindow &owner,
747 std::unique_ptr< TrackPanelAx > pAx
748)
749{
750#if wxUSE_ACCESSIBILITY
751 // wxWidgets owns the accessible object
752 owner.SetAccessible(mAx = pAx.release());
753#else
754 // wxWidgets does not own the object, but we need to retain it
755 mAx = std::move(pAx);
756#endif
757}
758
760{
761 if (mAx)
762 mAx->MessageForScreenReader( message );
763}
764
766{
767 if (mAx)
768 mAx->Updated();
769}
wxT("CloseDown"))
Toolkit-neutral facade for basic user interface services.
const TranslatableString name
Definition: Distortion.cpp:76
#define _(s)
Definition: Internat.h:73
const auto project
declares abstract base class Track, TrackList, and iterators over TrackList
static const AudacityProject::AttachedObjects::RegisteredFactory key
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:266
A LabelTrack is a Track that holds labels (LabelStruct).
Definition: LabelTrack.h:87
static wxString GetDefaultName()
Definition: LabelTrack.cpp:63
A Track that is used for Midi notes. (Somewhat old code).
Definition: NoteTrack.h:65
AudioTrack subclass that can also be audibly replayed by the program.
Definition: PlayableTrack.h:40
static bool IsSyncLockSelected(const Track *pTrack)
Definition: SyncLock.cpp:82
A kind of Track used to 'warp time'.
Definition: TimeTrack.h:24
static wxString GetDefaultName()
Definition: TimeTrack.cpp:36
~TrackFocus() override
void UpdateAccessibility()
void SetAccessible(wxWindow &owner, std::unique_ptr< TrackPanelAx > pAccessible)
void Set(Track *pTrack)
void MessageForScreenReader(const TranslatableString &message)
TrackFocus(AudacityProject &project)
std::unique_ptr< TrackPanelAx > mAx
Definition: TrackPanelAx.h:195
AudacityProject & mProject
Definition: TrackPanelAx.h:190
Track * Get()
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:123
void EnsureVisible(bool modifyState=false)
Definition: Track.cpp:86
std::shared_ptr< Subclass > SharedPointer()
Definition: Track.h:161
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:987
TrackIter< Track > Find(Track *pTrack)
Definition: Track.cpp:536
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1091
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:354
RectangleFinder mFinder
Definition: TrackPanelAx.h:141
TrackPanelAx(AudacityProject &project)
std::weak_ptr< Track > mFocusedTrack
Definition: TrackPanelAx.h:143
void MessageForScreenReader(const TranslatableString &message)
std::shared_ptr< Track > GetFocus()
int mNumFocusedTrack
Definition: TrackPanelAx.h:144
virtual ~TrackPanelAx()
AudacityProject & mProject
Definition: TrackPanelAx.h:135
TrackList & GetTracks()
int TrackNum(const std::shared_ptr< Track > &track)
wxString mMessage
Definition: TrackPanelAx.h:146
std::shared_ptr< Track > SetFocus(std::shared_ptr< Track > track={})
std::shared_ptr< Track > FindTrack(int num)
wxWindow * GetWindow() const
Definition: TrackPanelAx.h:125
Holds a msgid for the translation catalog; may also bind format arguments.
wxString Translation() const
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:208
std::unique_ptr< WindowPlacement > FindFocus()
Find the window that is accepting keyboard input, if any.
Definition: BasicUI.h:343
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
std::shared_ptr< Track > FindTrack(TrackPanelCell *pCell)
Definition: TrackPanel.cpp:525