Audacity 3.2.0
TrackPanel.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 TrackPanel.cpp
6
7 Dominic Mazzoni
8 and lots of other contributors
9
10 Implements TrackPanel.
11
12********************************************************************//***************************************************************//*****************************************************************//*****************************************************************/
45
46
47#include "TrackPanel.h"
48#include "TrackPanelConstants.h"
49
50#include <wx/setup.h> // for wxUSE_* macros
51
52#include "AdornedRulerPanel.h"
54#include "KeyboardCapture.h"
55#include "PendingTracks.h"
56#include "Project.h"
57#include "ProjectAudioIO.h"
58#include "ProjectAudioManager.h"
59#include "ProjectHistory.h"
60#include "ProjectWindows.h"
61#include "ProjectSettings.h"
62#include "ProjectStatus.h"
63#include "ProjectTimeRuler.h"
64#include "ProjectWindow.h"
65#include "SyncLock.h"
66#include "Theme.h"
67#include "TrackArt.h"
69
70#include "UndoManager.h"
71#include "UIHandle.h"
72
73#include "AColor.h"
74#include "AllThemeResources.h"
75#include "AudioIO.h"
76#include "float_cast.h"
77
78#include "Prefs.h"
79#include "RefreshCode.h"
80#include "TrackArtist.h"
81#include "TrackPanelAx.h"
83#include "Viewport.h"
84#include "WaveTrack.h"
85
86#include "FrameStatistics.h"
87
91
92//This loads the appropriate set of cursors, depending on platform.
93#include "../images/Cursors.h"
94
95#include <algorithm>
96
97#include <wx/dc.h>
98#include <wx/dcclient.h>
99#include <wx/graphics.h>
100
102
103static_assert( kVerticalPadding == kTopMargin + kBottomMargin );
104static_assert( kTrackInfoTitleHeight + kTrackInfoTitleExtra == kAffordancesAreaHeight, "Drag bar is misaligned with the menu button");
105
169// Is the distance between A and B less than D?
170template < class A, class B, class DIST > bool within(A a, B b, DIST d)
171{
172 return (a > b - d) && (a < b + d);
173}
174
175BEGIN_EVENT_TABLE(TrackPanel, CellularPanel)
176 EVT_MOUSE_EVENTS(TrackPanel::OnMouseEvent)
177 EVT_KEY_DOWN(TrackPanel::OnKeyDown)
178
179 EVT_PAINT(TrackPanel::OnPaint)
180
181 EVT_TIMER(wxID_ANY, TrackPanel::OnTimer)
182
183 EVT_SIZE(TrackPanel::OnSize)
184
186
187
189std::unique_ptr<wxCursor> MakeCursor( int WXUNUSED(CursorId), const char * const pXpm[36], int HotX, int HotY )
190{
191#define CURSORS_SIZE32
192#ifdef CURSORS_SIZE32
193 const int HotAdjust =0;
194#else
195 const int HotAdjust =8;
196#endif
197
198 wxImage Image = wxImage(wxBitmap(pXpm).ConvertToImage());
199 Image.SetMaskColour(255,0,0);
200 Image.SetMask();// Enable mask.
201
202 Image.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_X, HotX-HotAdjust );
203 Image.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_Y, HotY-HotAdjust );
204 return std::make_unique<wxCursor>( Image );
205}
206
207
208namespace{
209
210AttachedWindows::RegisteredFactory sKey{
211 []( AudacityProject &project ) -> wxWeakRef< wxWindow > {
213 auto &viewInfo = ViewInfo::Get( project );
214 auto &window = ProjectWindow::Get( project );
215 auto mainPage = window.GetTrackListWindow();
216 wxASSERT( mainPage ); // to justify safenew
217
218 auto &tracks = TrackList::Get( project );
219 auto result = safenew TrackPanel(mainPage,
220 window.NextWindowID(),
221 wxDefaultPosition,
222 wxDefaultSize,
223 tracks.shared_from_this(),
224 &viewInfo,
225 &project,
226 &ruler);
227 SetProjectPanel( project, *result );
228 return result;
229 }
230};
231
232}
233
235{
237}
238
240{
241 return Get( const_cast< AudacityProject & >( project ) );
242}
243
245{
246 auto *pPanel = GetAttachedWindows(project).Find<TrackPanel>( sKey );
247 if (pPanel) {
248 pPanel->wxWindow::Destroy();
250 }
251}
252
253// Don't warn us about using 'this' in the base member initializer list.
254#ifndef __WXGTK__ //Get rid if this pragma for gtk
255#pragma warning( disable: 4355 )
256#endif
257TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id,
258 const wxPoint & pos,
259 const wxSize & size,
260 const std::shared_ptr<TrackList> &tracks,
261 ViewInfo * viewInfo,
264 : CellularPanel(parent, id, pos, size, viewInfo,
265 wxWANTS_CHARS | wxNO_BORDER),
266 mTracks(tracks),
267 mRuler(ruler),
268 mTrackArtist(nullptr),
269 mRefreshBacking(false)
270#ifndef __WXGTK__ //Get rid if this pragma for gtk
271#pragma warning( default: 4355 )
272#endif
273{
274 SetLayoutDirection(wxLayout_LeftToRight);
275 SetLabel(XO("Track Panel"));
276 SetName(XO("Track Panel"));
277 SetBackgroundStyle(wxBG_STYLE_PAINT);
278
279#if wxUSE_ACCESSIBILITY
280 // Inject finder of track rectangles into the accessibility helper
281 {
282 const auto finder = [
283 weakThis = wxWeakRef<TrackPanel>{ this }
284 ] (const Track &track) -> wxRect {
285 if (weakThis)
286 return weakThis->FindTrackRect(&track);
287 return {};
288 };
289 auto &focus = TrackFocus::Get(*GetProject());
290 auto &viewport = Viewport::Get(*GetProject());
291 TrackPanelAx *pAx{};
292 SetAccessible(pAx =
294 viewport.weak_from_this(), focus.weak_from_this(), finder });
295 focus.SetCallbacks(std::make_unique<TrackPanelAx::Adapter>(pAx));
296 }
297#endif
298
299 mTrackArtist = std::make_unique<TrackArtist>( this );
300
301 mTimeCount = 0;
302 mTimer.parent = this;
303 // Timer is started after the window is visible
304 ProjectWindow::Get( *GetProject() ).Bind(wxEVT_IDLE,
305 &TrackPanel::OnIdle, this);
306
307 // Register for tracklist updates
309 .Subscribe([this](const TrackListEvent &event){
310 switch (event.mType) {
311 case TrackListEvent::RESIZING:
312 case TrackListEvent::ADDITION:
313 OnTrackListResizing(event); break;
314 case TrackListEvent::DELETION:
315 OnTrackListDeletion(); break;
316 default:
317 break;
318 }
319 });
320
321 auto theProject = GetProject();
324
326 .Subscribe(*this, &TrackPanel::OnTrackFocusChange);
327
330
333
335 .Subscribe([this](const RealtimeEffectManagerMessage& msg)
336 {
337 if (auto pTrack = dynamic_cast<Track *>(msg.group))
338 //update "effects" button
339 RefreshTrack(pTrack);
340 });
341
343 ProjectTimeRuler::Get(*theProject).GetRuler().Subscribe([this](auto mode) { Refresh(); });
345 .Subscribe([this](auto&){ Refresh(false); });
346
347 UpdatePrefs();
348}
349
350
352{
353 mTimer.Stop();
354
355 // This can happen if a label is being edited and the user presses
356 // ALT+F4 or Command+Q
357 if (HasCapture())
358 ReleaseMouse();
359}
360
362{
363 // All vertical rulers must be recalculated since the minimum and maximum
364 // frequencies may have been changed.
366
367 Refresh();
368}
369
373{
374 auto window = GetParent();
375
376 while(window != nullptr)
377 {
378 if(const auto projectWindow = dynamic_cast<ProjectWindow*>(window))
379 return projectWindow->FindProject().get();
380
381 window = window->GetParent();
382 }
383 return nullptr;
384}
385
386void TrackPanel::OnSize( wxSizeEvent &evt )
387{
388 evt.Skip();
389 const auto &size = evt.GetSize();
390 mViewInfo->SetWidth( size.GetWidth() );
391 mViewInfo->SetHeight( size.GetHeight() );
392}
393
394void TrackPanel::OnIdle(wxIdleEvent& event)
395{
396 event.Skip();
397 // The window must be ready when the timer fires (#1401)
398 if (IsShownOnScreen())
399 {
400 mTimer.Start(std::chrono::milliseconds{kTimerInterval}.count(), FALSE);
401
402 // Timer is started, we don't need the event anymore
403 GetProjectFrame( *GetProject() ).Unbind(wxEVT_IDLE,
404 &TrackPanel::OnIdle, this);
405 }
406 else
407 {
408 // Get another idle event, wx only guarantees we get one
409 // event after "some other normal events occur"
410 event.RequestMore();
411 }
412}
413
415void TrackPanel::OnTimer(wxTimerEvent& )
416{
417 mTimeCount++;
418
419 AudacityProject *const p = GetProject();
420 auto &window = ProjectWindow::Get( *p );
421 auto &viewport = Viewport::Get(*p);
422
423 auto &projectAudioIO = ProjectAudioIO::Get( *p );
424 auto gAudioIO = AudioIO::Get();
425
426 // Check whether we were playing or recording, but the stream has stopped.
427 if (projectAudioIO.GetAudioIOToken()>0 && !IsAudioActive())
428 {
429 //the stream may have been started up after this one finished (by some other project)
430 //in that case reset the buttons don't stop the stream
431 auto &projectAudioManager = ProjectAudioManager::Get( *p );
432 projectAudioManager.Stop(!gAudioIO->IsStreamActive());
433 }
434
435 // Next, check to see if we were playing or recording
436 // audio, but now Audio I/O is completely finished.
437 if (projectAudioIO.GetAudioIOToken()>0 &&
438 !gAudioIO->IsAudioTokenActive(projectAudioIO.GetAudioIOToken()))
439 {
440 projectAudioIO.SetAudioIOToken(0);
441 viewport.Redraw();
442 }
445 }
446
447 // Notify listeners for timer ticks
448 window.GetPlaybackScroller().OnTimer();
449
450 DrawOverlays(false);
451 mRuler->DrawOverlays(false);
452
453 if(IsAudioActive() && gAudioIO->GetNumCaptureChannels()) {
454
455 // Periodically update the display while recording
456
457 if ((mTimeCount % 5) == 0) {
458 // Must tell OnPaint() to recreate the backing bitmap
459 // since we've not done a full refresh.
460 mRefreshBacking = true;
461 Refresh( false );
462 }
463 }
464 if(mTimeCount > 1000)
465 mTimeCount = 0;
466}
467
469{
470 Refresh(false);
471}
472
474{
475 if (message.type == UndoRedoMessage::Reset) {
476 TrackFocus::Get( *GetProject() ).Set( nullptr );
477 Refresh( false );
478 }
479}
480
483void TrackPanel::OnPaint(wxPaintEvent & /* event */)
484{
485 // If the selected region changes - we must repaint the tracks, because the
486 // selection is baked into track image
488 {
489 mRefreshBacking = true;
491 }
492
493 auto sw =
495
496 {
497 wxPaintDC dc(this);
498
499 // Retrieve the damage rectangle
500 wxRect box = GetUpdateRegion().GetBox();
501
502 // Recreate the backing bitmap if we have a full refresh
503 // (See TrackPanel::Refresh())
504 if (mRefreshBacking || (box == GetRect()))
505 {
506 // Reset (should a mutex be used???)
507 mRefreshBacking = false;
508
509 // Redraw the backing bitmap
511
512 // Copy it to the display
513 DisplayBitmap(dc);
514 }
515 else
516 {
517 // Copy full, possibly clipped, damage rectangle
518 RepairBitmap(dc, box.x, box.y, box.width, box.height);
519 }
520
521 // Done with the clipped DC
522
523 // Drawing now goes directly to the client area.
524 // DrawOverlays() may need to draw outside the clipped region.
525 // (Used to make a NEW, separate wxClientDC, but that risks flashing
526 // problems on Mac.)
527 dc.DestroyClippingRegion();
528 DrawOverlays(true, &dc);
529 }
530}
531
533{
535}
536
537namespace {
538 std::shared_ptr<Track> FindTrack(TrackPanelCell *pCell)
539 {
540 if (pCell)
541 // FindTrack as applied through the CommonTrackPanelCell interface
542 // will really find a track, though for now it finds a left or right
543 // channel.
544 return static_cast<CommonTrackPanelCell*>(pCell)->FindTrack();
545 return {};
546 }
547}
548
550 (TrackPanelCell *pClickedCell, TrackPanelCell *pLatestCell,
551 UIHandle::Result refreshResult)
552{
553 const auto panel = this;
554 auto pLatestTrack = FindTrack( pLatestCell ).get();
555
556 // This precaution checks that the track is not only nonnull, but also
557 // really owned by the track list
558 auto pClickedTrack = GetTracks()->Lock(
559 std::weak_ptr<Track>{ FindTrack( pClickedCell ) }
560 ).get();
561
562 // TODO: make a finer distinction between refreshing the track control area,
563 // and the waveform area. As it is, redraw both whenever you must redraw either.
564
565 // Copy data from the underlying tracks to the pending tracks that are
566 // really displayed
567 PendingTracks::Get(*panel->GetProject()).UpdatePendingTracks();
568
569 using namespace RefreshCode;
570
571 if (refreshResult & DestroyedCell) {
572 panel->UpdateViewIfNoTracks();
573 // Beware stale pointer!
574 if (pLatestTrack == pClickedTrack)
575 pLatestTrack = nullptr;
576 pClickedTrack = nullptr;
577 }
578
579 if (pClickedTrack && (refreshResult & RefreshCode::UpdateVRuler))
580 panel->UpdateVRuler(pClickedTrack);
581
582 if (refreshResult & RefreshCode::DrawOverlays) {
583 panel->DrawOverlays(false);
584 mRuler->DrawOverlays(false);
585 }
586
587 // Refresh all if told to do so, or if told to refresh a track that
588 // is not known.
589 const bool refreshAll =
590 ( (refreshResult & RefreshAll)
591 || ((refreshResult & RefreshCell) && !pClickedTrack)
592 || ((refreshResult & RefreshLatestCell) && !pLatestTrack));
593
594 if (refreshAll)
595 panel->Refresh(false);
596 else {
597 if (refreshResult & RefreshCell)
598 panel->RefreshTrack(pClickedTrack);
599 if (refreshResult & RefreshLatestCell)
600 panel->RefreshTrack(pLatestTrack);
601 }
602
603 if (refreshResult & FixScrollbars)
604 panel->MakeParentRedrawScrollbars();
605
606 if (refreshResult & Resize)
608
609 if ((refreshResult & RefreshCode::EnsureVisible) && pClickedTrack) {
610 auto & focus = TrackFocus::Get(*GetProject());
611 focus.Set(pClickedTrack);
612 if (const auto pFocus = focus.Get())
614 }
615}
616
618{
621}
622
624{
627}
628
630{
632 return ProjectAudioIO::Get( *p ).IsAudioActive();
633}
634
636{
637 auto status = st;
638 if (HasEscape())
639 /* i18n-hint Esc is a key on the keyboard */
640 status.Join( XO("(Esc to cancel)"), " " );
641 ProjectStatus::Get( *GetProject() ).Set( status );
642}
643
645{
646 // Full refresh since the label area may need to indicate
647 // newly selected tracks.
648 Refresh(false);
649
650 // Make sure the ruler follows suit.
652}
653
654// Counts selected tracks, counting stereo tracks as one track.
656{
657 return GetTracks()->Selected().size();
658}
659
661{
662 if (mTracks->empty())
663 {
664 // BG: There are no more tracks on screen
665 //BG: Set zoom to normal
667
668 //STM: Set selection to 0,0
669 //PRL: and default the rest of the selection information
671
672 // PRL: Following causes the time ruler to align 0 with left edge.
673 // Bug 972
674 mViewInfo->hpos = 0;
675
677 //STM: Clear message if all tracks are removed
679 }
680}
681
682// The tracks positions within the list have changed, so update the vertical
683// ruler size for the track that triggered the event.
685{
686 auto t = e.mpTrack.lock();
687 // A deleted track can trigger the event. In which case do nothing here.
688 // A deleted track can have a valid pointer but no owner, bug 2060
689 if( t && t->HasOwner() )
690 UpdateVRuler(t.get());
691
692 // fix for bug 2477
694}
695
696// Tracks have been removed from the list.
698{
699 // copy shared_ptr for safety, as in HandleClick
700 auto handle = Target();
701 if (handle) {
702 handle->OnProjectChange(GetProject());
703 }
704
705 // If the focused track disappeared but there are still other tracks,
706 // this reassigns focus.
707 TrackFocus( *GetProject() ).Get();
708
710}
711
712void TrackPanel::OnKeyDown(wxKeyEvent & event)
713{
714 switch (event.GetKeyCode())
715 {
716 // Allow PageUp and PageDown keys to
717 //scroll the Track Panel left and right
718 case WXK_PAGEUP:
720 return;
721
722 case WXK_PAGEDOWN:
724 return;
725
726 default:
727 // fall through to base class handler
728 event.Skip();
729 }
730}
731
732void TrackPanel::OnMouseEvent(wxMouseEvent & event)
733{
734 if (event.LeftDown()) {
735 // wxTimers seem to be a little unreliable, so this
736 // "primes" it to make sure it keeps going for a while...
737
738 // When this timer fires, we call TrackPanel::OnTimer and
739 // possibly update the screen for offscreen scrolling.
740 mTimer.Stop();
741 mTimer.Start(std::chrono::milliseconds{kTimerInterval}.count(), FALSE);
742 }
743
744
745 if (event.ButtonUp()) {
746 //ShowTrack should be called after processing the up-click.
747 this->CallAfter( [this, event]{
748 const auto foundCell = FindCell(event.m_x, event.m_y);
749 const auto t = FindTrack( foundCell.pCell.get() );
750 if (t) {
751 auto &focus = TrackFocus::Get(*GetProject());
752 focus.Set(t.get());
754 }
755 } );
756 }
757
758 // Must also fall through to base class handler
759 event.Skip();
760}
761
763{
766}
767
768void TrackPanel::RefreshTrack(Track *trk, bool refreshbacking)
769{
770 if (!trk)
771 return;
772
773 auto height = ChannelView::GetChannelGroupHeight(trk);
774
775 // Set rectangle top according to the scrolling position, `vpos`
776 // Subtract the inset (above) and shadow (below) from the height of the
777 // rectangle, but not the border
778 // This matters because some separators do paint over the border
779 auto &view = ChannelView::Get(*trk->GetChannel(0));
780 const auto top =
781 -mViewInfo->vpos + view.GetCumulativeHeightBefore() + kTopInset;
782 height -= (kTopInset + kShadowThickness);
783
784 // Width also subtracts insets (left and right) plus shadow (right)
785 const auto left = kLeftInset;
786 const auto width = GetRect().GetWidth()
788
789 wxRect rect(left, top, width, height);
790
791 if( refreshbacking )
792 mRefreshBacking = true;
793
794 Refresh( false, &rect );
795}
796
797
802void TrackPanel::Refresh(bool eraseBackground /* = TRUE */,
803 const wxRect *rect /* = NULL */)
804{
805 // Tell OnPaint() to refresh the backing bitmap.
806 //
807 // Originally I had the check within the OnPaint() routine and it
808 // was working fine. That was until I found that, even though a full
809 // refresh was requested, Windows only set the onscreen portion of a
810 // window as damaged.
811 //
812 // So, if any part of the trackpanel was off the screen, full refreshes
813 // didn't work and the display got corrupted.
814 if( !rect || ( *rect == GetRect() ) )
815 {
816 mRefreshBacking = true;
817 }
818 wxWindow::Refresh(eraseBackground, rect);
819
820 CallAfter([this]{
821 if (GetProject())
823}
824
826{
828 return;
829 // Some hit tests want to change their cursor to and from the ban symbol
831}
832
834
839{
840 wxRegion region = GetUpdateRegion();
841
842 const wxRect clip = GetRect();
843
845 mTrackArtist->pSelectedRegion = &sr;
846 const auto &pendingTracks = PendingTracks::Get(*GetProject());
847 mTrackArtist->pPendingTracks = &pendingTracks;
848 mTrackArtist->pZoomInfo = mViewInfo;
850 *dc, Target(), mLastMouseState, mTrackArtist.get()
851 };
852
853 // Don't draw a bottom margin here.
854
855 const auto &settings = ProjectSettings::Get( *GetProject() );
856 bool bMultiToolDown =
857 (ToolCodes::multiTool == settings.GetTool());
858 bool envelopeFlag =
859 bMultiToolDown || (ToolCodes::envelopeTool == settings.GetTool());
860 bool bigPointsFlag =
861 bMultiToolDown || (ToolCodes::drawTool == settings.GetTool());
862 bool sliderFlag = bMultiToolDown;
863 bool brushFlag = false;
864#ifdef EXPERIMENTAL_BRUSH_TOOL
865 brushFlag = (ToolCodes::brushTool == settings.GetTool());
866#endif
867
868 const bool hasSolo = GetTracks()->Any<PlayableTrack>()
869 .any_of( [&](const PlayableTrack *pt) {
870 pt = static_cast<const PlayableTrack *>(
871 &pendingTracks.SubstitutePendingChangedTrack(*pt));
872 return pt->GetSolo();
873 } );
874
875 mTrackArtist->drawEnvelope = envelopeFlag;
876 mTrackArtist->bigPoints = bigPointsFlag;
877 mTrackArtist->drawSliders = sliderFlag;
878 mTrackArtist->onBrushTool = brushFlag;
879 mTrackArtist->hasSolo = hasSolo;
880
882}
883
885(const std::shared_ptr< CommonTrackPanelCell > &pCell)
886{
887 mpBackground = pCell;
888}
889
890std::shared_ptr< CommonTrackPanelCell > TrackPanel::GetBackgroundCell()
891{
892 return mpBackground;
893}
894
895namespace {
897{
898 auto channels = t.Channels();
899 assert(!channels.empty());
900
901 // Collect heights, and count affordances
902 int nAffordances = 0;
903 int totalHeight = 0;
904 std::vector<int> oldHeights;
905 for (auto pChannel : channels) {
906 auto &view = ChannelView::Get(*pChannel);
907 const auto height = view.GetHeight();
908 totalHeight += height;
909 oldHeights.push_back(height);
910 if (view.GetAffordanceControls())
911 ++nAffordances;
912 }
913
914 // Allocate results
915 auto nChannels = static_cast<int>(oldHeights.size());
916 std::vector<int> results;
917 results.reserve(nChannels);
918
919 // Now reallocate the channel heights for the presence of affordances
920 // and separators
921 auto availableHeight = totalHeight
922 - nAffordances * kAffordancesAreaHeight
923 - (nChannels - 1) * kChannelSeparatorThickness
925 int cumulativeOldHeight = 0;
926 int cumulativeNewHeight = 0;
927 for (const auto &oldHeight : oldHeights) {
928 // Preserve the porportions among the stored heights
929 cumulativeOldHeight += oldHeight;
930 const auto newHeight =
931 cumulativeOldHeight * availableHeight / totalHeight
932 - cumulativeNewHeight;
933 cumulativeNewHeight += newHeight;
934 results.push_back(newHeight);
935 }
936
937 return results;
938}
939}
940
942{
943 for (auto t : GetTracks()->Any<WaveTrack>())
945
947}
948
950{
951 if (t)
953
955}
956
958{
959 auto heights = FindAdjustedChannelHeights(t);
960
961 wxRect rect(mViewInfo->GetVRulerOffset(),
962 0,
964 0);
965
966 auto pHeight = heights.begin();
967 for (auto pChannel : t.Channels()) {
968 auto &view = ChannelView::Get(*pChannel);
969 const auto height = *pHeight++;
970 rect.SetHeight(height);
971 const auto subViews = view.GetSubViews(rect);
972 if (subViews.empty())
973 continue;
974
975 auto iter = subViews.begin(), end = subViews.end(), next = iter;
976 auto yy = iter->first;
977 wxSize vRulerSize{ 0, 0 };
978 auto &size = view.vrulerSize;
979 for (; iter != end; iter = next) {
980 ++next;
981 auto nextY = (next == end)
982 ? height
983 : next->first;
984 rect.SetHeight(nextY - yy);
985 // This causes ruler size in the track to be reassigned:
986 ChannelVRulerControls::Get(*iter->second).UpdateRuler(rect);
987 // But we want to know the maximum width and height over all sub-views:
988 vRulerSize.IncTo({ size.first, size.second });
989 yy = nextY;
990 }
991 size = { vRulerSize.x, vRulerSize.y };
992 }
993}
994
996{
997 auto trackRange = GetTracks()->Any();
998 if (trackRange) {
999 wxSize s{ 0, 0 };
1000 // Find maximum width over all channels
1001 for (auto t : trackRange)
1002 for (auto pChannel : t->Channels()) {
1003 const auto &size = ChannelView::Get(*pChannel).vrulerSize;
1004 s.IncTo({ size.first, size.second });
1005 }
1006
1007 if (mViewInfo->GetVRulerWidth() != s.GetWidth()) {
1008 mViewInfo->SetVRulerWidth(s.GetWidth());
1010 mViewInfo->GetLeftOffset()); // bevel on AdornedRuler
1011 mRuler->Refresh();
1012 }
1013 }
1014 Refresh(false);
1015}
1016
1018{
1020 t ? ChannelView::Get(*t->GetChannel(0)).shared_from_this() : nullptr);
1021}
1022
1023namespace {
1024 // Drawing constants
1025 // DisplaceX and MarginX are large enough to avoid overwriting <- symbol
1026 // See TrackArt::DrawNegativeOffsetTrackArrows
1027 enum : int {
1028 // Displacement of the rectangle from upper left corner
1030 // Size of margins about the text extent that determine the rectangle size
1032 // Derived constants
1034 };
1035
1037{
1038 // It is assumed that all channels we ever see are in groups that are
1039 // also Tracks
1040 return static_cast<Track &>(channel.GetChannelGroup());
1041}
1042
1043const Track &GetTrack(const Channel &channel)
1044{
1045 // It is assumed that all channels we ever see are in groups that are
1046 // also Tracks
1047 return static_cast<const Track &>(channel.GetChannelGroup());
1048}
1049
1051 wxDC &dc, const Channel &channel, wxCoord *pW, wxCoord *pH)
1052{
1053 wxFont labelFont(12, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1054 dc.SetFont(labelFont);
1055 dc.GetTextExtent(GetTrack(channel).GetName(), pW, pH);
1056}
1057
1059 int leftOffset,
1060 const wxRect &trackRect, wxCoord textWidth, wxCoord textHeight )
1061{
1062 return {
1063 leftOffset + DisplaceX,
1064 trackRect.y + DisplaceY,
1065 textWidth + MarginsX,
1066 textHeight + MarginsY
1067 };
1068}
1069
1070/*
1071
1072 The following classes define the subdivision of the area of the TrackPanel
1073 into cells with differing responses to mouse, keyboard, and scroll wheel
1074 events.
1075
1076 The classes defining the less inclusive areas are earlier, while those
1077 defining ever larger hierarchical groupings of cells are later.
1078
1079 To describe that subdivision again, informally, and top-down:
1080
1081 Firstly subtract margin areas, on the left and right, that do not interact.
1082
1083 Secondly subtract a noninterative margin above the first track, and an area
1084 below all tracks that causes deselection of all tracks if you click it.
1085 (One or both of those areas might be vertically scrolled off-screen, though.)
1086 Divide what remains into areas corresponding to the several tracks.
1087
1088 Thirdly, for each track, subtract an area below, which you can click and drag
1089 to resize the track vertically.
1090
1091 Fourthly, subtract an area at the left, which contains the track controls,
1092 such as the menu and delete and minimize buttons, and others appropriate
1093 to the track subtype.
1094
1095 Fifthly, divide what remains into the vertically stacked channels, if there
1096 are more than one, alternating with separators, which can be clicked to
1097 resize the channel views.
1098
1099 Sixthly, divide each channel into one or more vertically stacked sub-views.
1100
1101 Lastly, split the area for each sub-view into a vertical ruler, and an area
1102 that displays the channel's own contents.
1103
1104*/
1105
1107 std::vector< UIHandlePtr > HitTest(
1108 const TrackPanelMouseState &, const AudacityProject *) override
1109 { return {}; }
1110 virtual std::shared_ptr< Track > DoFindTrack() override { return {}; }
1111 static std::shared_ptr<EmptyCell> Instance()
1112 {
1113 static auto instance = std::make_shared< EmptyCell >();
1114 return instance;
1115 }
1116
1117 // TrackPanelDrawable implementation
1118 void Draw(
1119 TrackPanelDrawingContext &context,
1120 const wxRect &rect, unsigned iPass ) override
1121 {
1122 if ( iPass == TrackArtist::PassMargins ) {
1123 // Draw a margin area of TrackPanel
1124 auto dc = &context.dc;
1125
1126 AColor::TrackPanelBackground( dc, false );
1127 dc->DrawRectangle( rect );
1128 }
1129 }
1130};
1131
1132// A vertical ruler left of a channel
1135 const std::shared_ptr<ChannelView> &pView, wxCoord leftOffset)
1136 : mpView{ pView }, mLeftOffset{ leftOffset } {}
1137 Subdivision Children( const wxRect &rect ) override
1138 {
1139 return { Axis::X, Refinement{
1140 { rect.GetLeft(),
1141 ChannelVRulerControls::Get(*mpView).shared_from_this() },
1142 { mLeftOffset, mpView }
1143 } };
1144 }
1145 std::shared_ptr<ChannelView> mpView;
1147};
1148
1149// One or more sub-views of one channel, stacked vertically, each containing
1150// a vertical ruler and a channel
1153 const std::shared_ptr<Channel> &pChannel,
1154 ChannelView::Refinement refinement, wxCoord leftOffset)
1155 : mpChannel{ pChannel }
1156 , mRefinement{ std::move( refinement ) }
1157 , mLeftOffset{ leftOffset } {}
1158 Subdivision Children( const wxRect &rect ) override
1159 {
1160 Refinement refinement;
1161 auto y1 = rect.GetTop();
1162 for ( const auto &subView : mRefinement ) {
1163 y1 = std::max( y1, subView.first );
1164 refinement.emplace_back( y1,
1165 std::make_shared< VRulerAndChannel >(
1166 subView.second, mLeftOffset ) );
1167 }
1168 return { Axis::Y, std::move( refinement ) };
1169 }
1170
1171 // TrackPanelDrawable implementation
1172 void Draw(
1173 TrackPanelDrawingContext &context,
1174 const wxRect &rect, unsigned iPass ) override
1175 {
1176 // This overpaints the track area, but sometimes too the stereo channel
1177 // separator, so draw at least later than that
1178
1179 if ( iPass == TrackArtist::PassBorders ) {
1180 if (mRefinement.size() > 1) {
1181 // Draw lines separating sub-views
1182 auto &dc = context.dc;
1183 AColor::CursorColor( &dc );
1184 auto iter = mRefinement.begin() + 1, end = mRefinement.end();
1185 for ( ; iter != end; ++iter ) {
1186 auto yy = iter->first;
1187 AColor::Line(dc, rect.x, yy, mLeftOffset - 1, yy);
1188 AColor::Line( dc, mLeftOffset, yy, rect.GetRight(), yy );
1189 }
1190 }
1191 }
1192 }
1193
1194 std::shared_ptr<Channel> mpChannel;
1197};
1198
1199//Simply place children one after another horizontally, without any specific logic
1201
1203
1205 : mRefinement(std::move(refinement))
1206 {
1207 }
1208
1209 Subdivision Children(const wxRect& /*rect*/) override
1210 {
1211 return { Axis::X, mRefinement };
1212 }
1213
1214};
1215
1216
1217// optional affordance area, and n channels with vertical rulers,
1218// alternating with n - 1 resizers;
1219// each channel-ruler pair might be divided into multiple views
1221 ChannelStack(const std::shared_ptr<Track> &pTrack, wxCoord leftOffset)
1222 : mpTrack{ pTrack }, mLeftOffset{ leftOffset } {}
1223 Subdivision Children(const wxRect &rect_) override
1224 {
1225 auto rect = rect_;
1226 Refinement refinement;
1227
1228 const auto channels = mpTrack->Channels();
1229 const auto pLast = *channels.rbegin();
1230 wxCoord yy = rect.GetTop();
1231 auto heights = FindAdjustedChannelHeights(*mpTrack);
1232 auto pHeight = heights.begin();
1233 for (auto pChannel : channels) {
1234 auto &view = ChannelView::Get(*pChannel);
1235 if (auto affordance = view.GetAffordanceControls()) {
1236 Refinement hgroup {
1237 std::make_pair(mLeftOffset, affordance)
1238 };
1239 refinement
1240 .emplace_back(yy, std::make_shared<HorizontalGroup>(hgroup));
1242 }
1243
1244 auto height = *pHeight++;
1245 rect.SetTop(yy);
1246 rect.SetHeight(height - kChannelSeparatorThickness);
1247 refinement.emplace_back(yy,
1248 std::make_shared<VRulersAndChannels>(pChannel,
1249 ChannelView::Get(*pChannel).GetSubViews(rect),
1250 mLeftOffset));
1251 if (pChannel != pLast) {
1252 yy += height;
1253 refinement.emplace_back(
1256 .shared_from_this());
1257 }
1258 }
1259
1260 return { Axis::Y, std::move(refinement) };
1261 }
1262
1264 const wxRect& rect, unsigned iPass) override
1265 {
1266 TrackPanelGroup::Draw(context, rect, iPass);
1267 if (iPass == TrackArtist::PassTracks)
1268 {
1269 auto vRulerRect = rect;
1270 vRulerRect.width = mLeftOffset - rect.x;
1271
1272 auto dc = &context.dc;
1273
1274 // Paint the background;
1275 AColor::MediumTrackInfo(dc, mpTrack->GetSelected() );
1276 dc->DrawRectangle( vRulerRect );
1277
1278 const auto channels = mpTrack->Channels();
1279 auto& view = ChannelView::Get(**channels.begin());
1280 if(auto affordance = view.GetAffordanceControls())
1281 {
1282 const auto yy = vRulerRect.y + kAffordancesAreaHeight - 1;
1283 AColor::Dark( dc, false );
1284 AColor::Line( *dc, vRulerRect.GetLeft(), yy, vRulerRect.GetRight(), yy );
1285 }
1286
1287 // Stroke left and right borders
1288 dc->SetPen(*wxBLACK_PEN);
1289
1290 AColor::Line( *dc, vRulerRect.GetLeftTop(), vRulerRect.GetLeftBottom() );
1291 AColor::Line( *dc, vRulerRect.GetRightTop(), vRulerRect.GetRightBottom() );
1292 }
1293 if (iPass == TrackArtist::PassFocus && mpTrack->IsSelected()) {
1294 const auto channels = mpTrack->Channels();
1295 const auto pLast = *channels.rbegin();
1296 wxCoord yy = rect.GetTop();
1297 auto heights = FindAdjustedChannelHeights(*mpTrack);
1298 auto pHeight = heights.begin();
1299 for (auto pChannel : channels) {
1300 auto& view = ChannelView::Get(*pChannel);
1301 auto height = *pHeight++;
1302 if (auto affordance = view.GetAffordanceControls())
1303 height += kAffordancesAreaHeight;
1304 auto trackRect = wxRect(
1305 mLeftOffset,
1306 yy,
1307 rect.GetRight() - mLeftOffset,
1309 TrackArt::DrawCursor(context, trackRect, mpTrack.get());
1310 yy += height;
1311 }
1312 }
1313 }
1314
1315 const std::shared_ptr<Track> mpTrack;
1317};
1318
1319// A track control panel, left of n vertical rulers and n channels
1320// alternating with n - 1 resizers
1323 const std::shared_ptr<Track> &pTrack, wxCoord leftOffset)
1324 : mpTrack{ pTrack }, mLeftOffset{ leftOffset } {}
1325 Subdivision Children(const wxRect &rect) override
1326 { return { Axis::X, Refinement{
1327 { rect.GetLeft(),
1328 TrackControls::Get(*mpTrack).shared_from_this() },
1329 { rect.GetLeft() + kTrackInfoWidth,
1330 std::make_shared<ChannelStack>(mpTrack, mLeftOffset) }
1331 } }; }
1332
1333 // TrackPanelDrawable implementation
1335 const wxRect &rect, unsigned iPass) override
1336 {
1337 if (iPass == TrackArtist::PassBorders) {
1338 auto &dc = context.dc;
1339 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1340 dc.SetPen(*wxBLACK_PEN);
1341
1342 // border
1343 dc.DrawRectangle(
1344 rect.x, rect.y,
1345 rect.width - kShadowThickness, rect.height - kShadowThickness
1346 );
1347
1348 // shadow
1349 if constexpr (kShadowThickness > 0)
1350 {
1351 // Stroke lines along bottom and right, which are slightly short at
1352 // bottom-left and top-right
1353 const auto right = rect.GetRight();
1354 const auto bottom = rect.GetBottom();
1355
1356 // bottom
1357 AColor::Line(dc, rect.x + 2, bottom, right, bottom);
1358 // right
1359 AColor::Line(dc, right, rect.y + 2, right, bottom);
1360 }
1361 }
1362 if (iPass == TrackArtist::PassFocus) {
1363 // Sometimes highlight is not drawn on backing bitmap. I thought
1364 // it was because FindFocus did not return the TrackPanel on Mac, but
1365 // when I removed that test, yielding this condition:
1366 // if (GetFocusedTrack() != NULL) {
1367 // the highlight was reportedly drawn even when something else
1368 // was the focus and no highlight should be drawn. -RBD
1369 const auto artist = TrackArtist::Get(context);
1370 auto &trackPanel = *artist->parent;
1371 auto &trackFocus = TrackFocus::Get( *trackPanel.GetProject() );
1372 if (trackFocus.Get() == mpTrack.get() &&
1373 wxWindow::FindFocus() == &trackPanel) {
1375 wxRect theRect = rect;
1376 auto &dc = context.dc;
1377 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1378
1379 AColor::TrackFocusPen(&dc, 2);
1380 dc.DrawRectangle(theRect);
1381 theRect.Deflate(1);
1382
1383 AColor::TrackFocusPen(&dc, 1);
1384 dc.DrawRectangle(theRect);
1385 theRect.Deflate(1);
1386
1387 AColor::TrackFocusPen(&dc, 0);
1388 dc.DrawRectangle(theRect);
1389 }
1390 }
1391 }
1392
1394 const wxRect &rect, const wxRect &, unsigned iPass) override
1395 {
1396 if (iPass == TrackArtist::PassBorders)
1397 return {
1398 rect.x - kBorderThickness,
1399 rect.y - kBorderThickness,
1400 rect.width + 2 * kBorderThickness + kShadowThickness,
1401 rect.height + 2 * kBorderThickness + kShadowThickness
1402 };
1403 else if (iPass == TrackArtist::PassFocus) {
1404 constexpr auto extra = kBorderThickness + 3;
1405 return {
1406 rect.x - extra,
1407 rect.y - extra,
1408 rect.width + 2 * extra + kShadowThickness,
1409 rect.height + 2 * extra + kShadowThickness
1410 };
1411 }
1412 else
1413 return rect;
1414 }
1415
1416 const std::shared_ptr<Track> mpTrack;
1418};
1419
1420// Stacks a label and a single or multi-channel track on a resizer below,
1421// which is associated with the last channel
1424 const std::shared_ptr<Track> &pTrack, wxCoord leftOffset)
1425 : mpTrack{ pTrack }, mLeftOffset{ leftOffset } {}
1426 Subdivision Children(const wxRect &rect) override
1427 { return { Axis::Y, Refinement{
1428 { rect.GetTop(),
1429 std::make_shared<LabeledChannelGroup>(mpTrack, mLeftOffset) },
1430 { rect.GetTop() + rect.GetHeight() - kTrackSeparatorThickness,
1432 **mpTrack->Channels().rbegin()).shared_from_this()
1433 }
1434 } }; }
1435 const std::shared_ptr<Track> mpTrack;
1437};
1438
1439// Stacks a dead area at top, the tracks, and the click-to-deselect area below
1441 explicit Subgroup(TrackPanel &panel) : mPanel{ panel } {}
1442 Subdivision Children(const wxRect &rect) override
1443 {
1444 const auto &viewInfo = *mPanel.GetViewInfo();
1445 wxCoord yy = -viewInfo.vpos;
1446 Refinement refinement;
1447
1448 auto &tracks = *mPanel.GetTracks();
1449 if (!tracks.empty())
1450 refinement.emplace_back( yy, EmptyCell::Instance() ),
1451 yy += kTopMargin;
1452
1453 for (const auto pTrack : tracks) {
1454 wxCoord height = 0;
1455 for (auto pChannel : pTrack->Channels()) {
1456 auto &view = ChannelView::Get(*pChannel);
1457 height += view.GetHeight();
1458 }
1459 refinement.emplace_back( yy,
1460 std::make_shared<ResizingChannelGroup>(
1461 pTrack->SharedPointer(), viewInfo.GetLeftOffset())
1462 );
1463 yy += height;
1464 }
1465
1466 refinement.emplace_back(std::max(0, yy), mPanel.GetBackgroundCell());
1467
1468 return { Axis::Y, std::move(refinement) };
1469 }
1471};
1472
1473// Main group shaves off the left and right margins
1475 explicit MainGroup(TrackPanel &panel) : mPanel{ panel } {}
1476 Subdivision Children(const wxRect &rect) override
1477 { return { Axis::X, Refinement{
1478 { 0, EmptyCell::Instance() },
1479 { kLeftMargin, std::make_shared<Subgroup>(mPanel) },
1480 { rect.GetRight() + 1 - kRightMargin, EmptyCell::Instance() }
1481 } }; }
1483};
1484
1485}
1486
1487std::shared_ptr<TrackPanelNode> TrackPanel::Root()
1488{
1489 // Root and other subgroup objects are throwaways.
1490 // They might instead be cached to avoid repeated allocation.
1491 // That cache would need invalidation when there is addition, deletion, or
1492 // permutation of tracks, or change of width of the vertical rulers.
1493 return std::make_shared< MainGroup >( *this );
1494}
1495
1496// This finds the rectangle of a given track (including all channels),
1497// either that of the label 'adornment' or the track itself
1498// The given track is assumed to be the first channel
1499wxRect TrackPanel::FindTrackRect( const Track * target )
1500{
1501 return CellularPanel::FindRect( [&] ( TrackPanelNode &node ) {
1502 if (auto pGroup = dynamic_cast<const LabeledChannelGroup*>( &node ))
1503 return pGroup->mpTrack.get() == target;
1504 return false;
1505 } );
1506}
1507
1509{
1510 auto rect = FindTrackRect(target);
1511 if (rect != wxRect{}) {
1512 // Enlarge horizontally.
1513 // PRL: perhaps it's one pixel too much each side, including some gray
1514 // beyond the yellow?
1515 rect.x = 0;
1516 GetClientSize(&rect.width, nullptr);
1517
1518 // Enlarge vertically, enough to enclose the yellow focus border pixels
1519 // The the outermost ring of gray pixels is included on three sides
1520 // but not the top (should that be fixed?)
1521
1522 // (Note that TrackPanel paints its focus over the "top margin" of the
1523 // rectangle allotted to the track, according to ChannelView::GetY() and
1524 // ChannelView::GetHeight(), but also over the margin of the next track.)
1525
1526 rect.height += kBottomMargin;
1527 int dy = kTopMargin - 1;
1528 rect.Inflate( 0, dy );
1529
1530 // Note that this rectangle does not coincide with any one of
1531 // the nodes in the subdivision.
1532 }
1533 return rect;
1534}
1535
1536std::vector<wxRect> TrackPanel::FindRulerRects(const Channel &target)
1537{
1538 std::vector<wxRect> results;
1539 VisitCells( [&](const wxRect &rect, TrackPanelCell &visited) {
1540 if (auto pRuler = dynamic_cast<const ChannelVRulerControls*>(&visited))
1541 if (auto pView = pRuler->GetChannelView())
1542 if (pView->FindChannel().get() == &target)
1543 results.push_back(rect);
1544 } );
1545 return results;
1546}
1547
1548std::shared_ptr<TrackPanelCell> TrackPanel::GetFocusedCell()
1549{
1550 auto pTrack = TrackFocus::Get(*GetProject()).Get();
1551 return pTrack
1552 ? ChannelView::Get(*pTrack->GetChannel(0)).shared_from_this()
1554}
1555
1557{
1558 // This may have a side-effect of assigning a focus if there was none
1559 auto& trackFocus = TrackFocus::Get(*GetProject());
1560 trackFocus.Set(trackFocus.Get());
1562}
1563
1565{
1566 if (message.focusPanel)
1567 SetFocus();
1568 if (auto cell = GetFocusedCell())
1569 Refresh(false);
1570}
wxImage(22, 22)
END_EVENT_TABLE()
XO("Cut/Copy/Paste")
#define safenew
Definition: MemoryX.h:10
AUDACITY_DLL_API void SetProjectPanel(AudacityProject &project, wxWindow &panel)
AUDACITY_DLL_API wxFrame & GetProjectFrame(AudacityProject &project)
Get the top-level window associated with the project (as a wxFrame only, when you do not need to use ...
AUDACITY_DLL_API AttachedWindows & GetAttachedWindows(AudacityProject &project)
accessors for certain important windows associated with each project
const auto tracks
const auto project
constexpr auto kTimerInterval
#define A(N)
Definition: ToChars.cpp:62
static Settings & settings()
Definition: TrackInfo.cpp:51
std::unique_ptr< wxCursor > MakeCursor(int WXUNUSED(CursorId), const char *const pXpm[36], int HotX, int HotY)
Definition: TrackPanel.cpp:189
bool within(A a, B b, DIST d)
Definition: TrackPanel.cpp:170
@ kBottomMargin
@ kTopInset
@ kTrackSeparatorThickness
@ kTopMargin
@ kChannelSeparatorThickness
@ kAffordancesAreaHeight
@ kVerticalPadding
Definition: ViewInfo.h:92
@ kTrackInfoTitleHeight
Definition: ViewInfo.h:96
@ kTrackInfoTitleExtra
Definition: ViewInfo.h:97
int id
@ kRightMargin
Definition: ZoomInfo.h:28
@ kTrackInfoWidth
Definition: ZoomInfo.h:30
@ kRightInset
Definition: ZoomInfo.h:26
@ kShadowThickness
Definition: ZoomInfo.h:23
@ kBorderThickness
Definition: ZoomInfo.h:22
@ kLeftMargin
Definition: ZoomInfo.h:27
@ kLeftInset
Definition: ZoomInfo.h:25
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:194
static void CursorColor(wxDC *dc)
Definition: AColor.cpp:437
static void MediumTrackInfo(wxDC *dc, bool selected)
Definition: AColor.cpp:415
static void Dark(wxDC *dc, bool selected, bool highlight=false)
Definition: AColor.cpp:421
static void TrackFocusPen(wxDC *dc, int level)
Definition: AColor.cpp:455
static void TrackPanelBackground(wxDC *dc, bool selected)
Definition: AColor.cpp:432
This is an Audacity Specific ruler panel which additionally has border, selection markers,...
void Refresh(bool eraseBackground=true, const wxRect *rect=(const wxRect *) NULL) override
void SetLeftOffset(int offset)
static AdornedRulerPanel & Get(AudacityProject &project)
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
static AudioIO * Get()
Definition: AudioIO.cpp:126
wxDC & GetBackingDCForRepaint()
Definition: BackedPanel.cpp:35
void DisplayBitmap(wxDC &dc)
Definition: BackedPanel.cpp:65
void RepairBitmap(wxDC &dc, wxCoord x, wxCoord y, wxCoord width, wxCoord height)
Definition: BackedPanel.cpp:60
Formerly part of TrackPanel, this abstract base class has no special knowledge of Track objects and i...
Definition: CellularPanel.h:34
void Draw(TrackPanelDrawingContext &context, unsigned nPasses)
void VisitCells(const SimpleCellVisitor &visitor)
UIHandlePtr Target()
ViewInfo * mViewInfo
FoundCell FindCell(int mouseX, int mouseY)
wxRect FindRect(const TrackPanelCell &cell)
wxMouseState mLastMouseState
void DoContextMenu(std::shared_ptr< TrackPanelCell > pCell)
wxCoord MostRecentXCoord() const
void HandleCursorForPresentMouseState(bool doHit=true)
IteratorRange< ChannelIterator< ChannelType > > Channels()
Get range of channels with mutative access.
Definition: Channel.h:384
std::shared_ptr< ChannelType > GetChannel(size_t iChannel)
Retrieve a channel, cast to the given type.
Definition: Channel.h:323
ChannelGroup & GetChannelGroup()
Channel object's lifetime is assumed to be nested in its Track's.
Definition: Channel.cpp:43
static ChannelVRulerControls & Get(ChannelView &)
virtual void UpdateRuler(const wxRect &rect)=0
static ChannelView & Get(Channel &channel)
std::vector< std::pair< wxCoord, std::shared_ptr< ChannelView > > > Refinement
Definition: ChannelView.h:121
virtual Refinement GetSubViews(const wxRect &rect)
std::pair< int, int > vrulerSize
Definition: ChannelView.h:129
static int GetChannelGroupHeight(const Track *pTrack)
Definition: ChannelView.cpp:39
Subclass * Find(const RegisteredFactory &key)
Get a (bare) pointer to an attachment, or null, down-cast it to Subclass *; will not create on demand...
Definition: ClientData.h:342
Subclass & Get(const RegisteredFactory &key)
Get reference to an attachment, creating on demand if not present, down-cast it to Subclass.
Definition: ClientData.h:318
void Assign(const RegisteredFactory &key, ReplacementPointer &&replacement)
Reassign Site's pointer to ClientData.
Definition: ClientData.h:364
@ TrackPanel
Full repaint time of the TrackPanel.
static Stopwatch CreateStopwatch(SectionID section) noexcept
Create a Stopwatch for the section specified.
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
Definition: Observer.h:199
void DrawOverlays(bool repaint_all, wxDC *pDC=nullptr)
static PendingTracks & Get(AudacityProject &project)
void UpdatePendingTracks()
AudioTrack subclass that can also be audibly replayed by the program.
Definition: PlayableTrack.h:40
bool GetSolo() const
Definition: PlayableTrack.h:48
bool IsAudioActive() const
static ProjectAudioIO & Get(AudacityProject &project)
static ProjectAudioManager & Get(AudacityProject &project)
static ProjectSettings & Get(AudacityProject &project)
static ProjectStatus & Get(AudacityProject &project)
void Set(const TranslatableString &msg, StatusBarField field=MainStatusBarField())
static ProjectTimeRuler & Get(AudacityProject &project)
A top-level window associated with a project, and handling scrollbars and zooming.
Definition: ProjectWindow.h:36
static ProjectWindow & Get(AudacityProject &project)
static RealtimeEffectManager & Get(AudacityProject &project)
Defines a selected portion of a project.
static SyncLockState & Get(AudacityProject &project)
Definition: SyncLock.cpp:27
static TrackArtist * Get(TrackPanelDrawingContext &)
Definition: TrackArtist.cpp:81
static TrackControls & Get(Track &track)
static TrackFocus & Get(AudacityProject &project)
Definition: TrackFocus.cpp:132
Track * Get()
Definition: TrackFocus.cpp:156
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:110
std::shared_ptr< Subclass > Lock(const std::weak_ptr< Subclass > &wTrack)
Definition: Track.h:1079
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:950
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:314
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:967
Helper to TrackPanel to give accessibility.
Definition: TrackPanelAx.h:34
virtual void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass)
std::pair< Axis, Refinement > Subdivision
std::vector< Child > Refinement
The TrackPanel class coordinates updates and operations on the main part of the screen which contains...
Definition: TrackPanel.h:63
void UpdateVRuler(Track *t)
Definition: TrackPanel.cpp:949
void UpdatePrefs() override
Definition: TrackPanel.cpp:361
Observer::Subscription mProjectRulerInvalidatedSubscription
Definition: TrackPanel.h:188
void HandlePageUpKey()
Definition: TrackPanel.cpp:617
AdornedRulerPanel * mRuler
Definition: TrackPanel.h:193
size_t GetSelectedTrackCount() const
Definition: TrackPanel.cpp:655
void UpdateViewIfNoTracks()
Definition: TrackPanel.cpp:660
std::shared_ptr< TrackList > mTracks
Definition: TrackPanel.h:191
double GetMostRecentXPos()
Definition: TrackPanel.cpp:762
virtual ~TrackPanel()
Definition: TrackPanel.cpp:351
std::shared_ptr< TrackPanelNode > Root() override
void ProcessUIHandleResult(TrackPanelCell *pClickedTrack, TrackPanelCell *pLatestCell, unsigned refreshResult) override
Definition: TrackPanel.cpp:550
Observer::Subscription mAudioIOSubscription
Definition: TrackPanel.h:183
std::shared_ptr< TrackPanelCell > GetFocusedCell() override
std::shared_ptr< CommonTrackPanelCell > GetBackgroundCell()
Definition: TrackPanel.cpp:890
AudacityProject * GetProject() const override
Definition: TrackPanel.cpp:372
void OnSize(wxSizeEvent &)
Definition: TrackPanel.cpp:386
Observer::Subscription mTrackListSubscription
Definition: TrackPanel.h:182
SelectedRegion mLastDrawnSelectedRegion
Definition: TrackPanel.h:220
void Refresh(bool eraseBackground=true, const wxRect *rect=(const wxRect *) NULL) override
Definition: TrackPanel.cpp:802
void OnTrackMenu(Track *t=NULL)
const TrackList * GetTracks() const
Definition: TrackPanel.h:163
static TrackPanel & Get(AudacityProject &project)
Definition: TrackPanel.cpp:234
void OnTrackFocusChange(struct TrackFocusChangeMessage)
void UpdateSelectionDisplay()
Definition: TrackPanel.cpp:644
void HandlePageDownKey()
Definition: TrackPanel.cpp:623
Observer::Subscription mRealtimeEffectManagerSubscription
Definition: TrackPanel.h:186
std::shared_ptr< CommonTrackPanelCell > mpBackground
Definition: TrackPanel.h:224
wxRect FindFocusedTrackRect(const Track *target)
Observer::Subscription mSyncLockSubscription
Definition: TrackPanel.h:187
void OnTimer(wxTimerEvent &event)
AS: This gets called on our wx timer events.
Definition: TrackPanel.cpp:415
void OnKeyDown(wxKeyEvent &event)
Definition: TrackPanel.cpp:712
void SetFocusedCell() override
void UpdateStatusMessage(const TranslatableString &status) override
Definition: TrackPanel.cpp:635
static void Destroy(AudacityProject &project)
Definition: TrackPanel.cpp:244
void UpdateVRulerSize()
Definition: TrackPanel.cpp:995
void OnTrackListDeletion()
Definition: TrackPanel.cpp:697
bool mRefreshBacking
Definition: TrackPanel.h:215
wxRect FindTrackRect(const Track *target)
std::vector< wxRect > FindRulerRects(const Channel &target)
void OnMouseEvent(wxMouseEvent &event)
Definition: TrackPanel.cpp:732
void DrawTracks(wxDC *dc)
Definition: TrackPanel.cpp:838
void SetBackgroundCell(const std::shared_ptr< CommonTrackPanelCell > &pCell)
Definition: TrackPanel.cpp:885
TrackPanel::AudacityTimer mTimer
void OnUndoReset(struct UndoRedoMessage)
Definition: TrackPanel.cpp:473
Observer::Subscription mUndoSubscription
Definition: TrackPanel.h:184
void UpdateVRulers()
Definition: TrackPanel.cpp:941
int mTimeCount
Definition: TrackPanel.h:213
void OnAudioIO(AudioIOEvent)
Definition: TrackPanel.cpp:825
TrackPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, const std::shared_ptr< TrackList > &tracks, ViewInfo *viewInfo, AudacityProject *project, AdornedRulerPanel *ruler)
Definition: TrackPanel.cpp:257
void OnSyncLockChange(struct SyncLockChangeMessage)
Definition: TrackPanel.cpp:468
bool IsAudioActive()
Definition: TrackPanel.cpp:629
void MakeParentRedrawScrollbars()
Definition: TrackPanel.cpp:532
void OnIdle(wxIdleEvent &event)
Definition: TrackPanel.cpp:394
void OnPaint(wxPaintEvent &event)
Definition: TrackPanel.cpp:483
Observer::Subscription mFocusChangeSubscription
Definition: TrackPanel.h:185
void RefreshTrack(Track *trk, bool refreshbacking=true)
Definition: TrackPanel.cpp:768
void UpdateTrackVRuler(Track &t)
Definition: TrackPanel.cpp:957
std::unique_ptr< TrackArtist > mTrackArtist
Definition: TrackPanel.h:195
Observer::Subscription mSelectionSubscription
Definition: TrackPanel.h:189
void OnTrackListResizing(const TrackListEvent &event)
Definition: TrackPanel.cpp:684
The TrackPanel is built up of nodes, subtrees of the CellularPanel's area Common base class for Track...
static TrackPanelResizerCell & Get(Channel &channel)
Holds a msgid for the translation catalog; may also bind format arguments.
TranslatableString & Join(TranslatableString arg, const wxString &separator={}) &
Append another translatable string.
unsigned Result
Definition: UIHandle.h:40
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:71
int vpos
Pixel distance from top of tracks to top of visible scrolled area.
Definition: ViewInfo.h:222
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:216
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
void SetHeight(int height)
Definition: ViewInfo.h:203
void UpdateScrollbarsForTracks()
Definition: Viewport.cpp:250
void ShowTrack(const Track &track)
Definition: Viewport.cpp:460
void SetHorizontalThumb(double scrollto, bool doScroll=true)
Definition: Viewport.cpp:201
void HandleResize()
Definition: Viewport.cpp:393
static Viewport & Get(AudacityProject &project)
Definition: Viewport.cpp:33
double PositionToTime(int64 position, int64 origin=0, bool ignoreFisheye=false) const
Definition: ZoomInfo.cpp:34
static double GetDefaultZoom()
Definition: ZoomInfo.h:116
void SetWidth(int width)
Definition: ZoomInfo.h:89
double GetScreenEndTime() const
Definition: ZoomInfo.h:107
int GetVRulerOffset() const
Definition: ZoomInfo.h:93
void SetVRulerWidth(int width)
Definition: ZoomInfo.h:92
int GetLeftOffset() const
Definition: ZoomInfo.h:96
double hpos
Leftmost visible timeline position in seconds.
Definition: ZoomInfo.h:54
void SetZoom(double pixelsPerSecond)
Definition: ZoomInfo.cpp:88
int GetVRulerWidth() const
Definition: ZoomInfo.h:91
void SetLabel(const TranslatableString &label)
void SetFocus(const WindowPlacement &focus)
Set the window that accepts keyboard input.
Definition: BasicUI.h:392
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:214
std::unique_ptr< WindowPlacement > FindFocus()
Find the window that is accepting keyboard input, if any.
Definition: BasicUI.h:383
void Capture(wxWindow *handler)
Namespace containing an enum 'what to do on a refresh?'.
Definition: RefreshCode.h:16
@ RefreshLatestCell
Definition: RefreshCode.h:25
AUDACITY_DLL_API void DrawCursor(TrackPanelDrawingContext &context, const wxRect &rect, const Track *track)
Definition: TrackArt.cpp:756
const Track & GetTrack(const Channel &channel)
AttachedWindows::RegisteredFactory sKey
Definition: TrackPanel.cpp:210
std::shared_ptr< Track > FindTrack(TrackPanelCell *pCell)
Definition: TrackPanel.cpp:538
void GetTrackNameExtent(wxDC &dc, const Channel &channel, wxCoord *pW, wxCoord *pH)
wxRect GetTrackNameRect(int leftOffset, const wxRect &trackRect, wxCoord textWidth, wxCoord textHeight)
std::vector< int > FindAdjustedChannelHeights(Track &t)
Definition: TrackPanel.cpp:896
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
STL namespace.
enum AudioIOEvent::Type type
Posted when effect is being added or removed to/from channel group or project.
ChannelGroup * group
null, if changes happened in the project scope
Sent after sync lock setting changes, with its new state.
Definition: SyncLock.h:20
bool focusPanel
whether to focus the window that shows tracks
Definition: TrackFocus.h:25
Notification of changes in individual tracks of TrackList, or of TrackList's composition.
Definition: Track.h:803
const std::weak_ptr< Track > mpTrack
Definition: Track.h:838
const Type mType
Definition: Track.h:837
Type of message published by UndoManager.
Definition: UndoManager.h:55
enum UndoRedoMessage::Type type
ChannelStack(const std::shared_ptr< Track > &pTrack, wxCoord leftOffset)
void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass) override
Subdivision Children(const wxRect &rect_) override
void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass) override
static std::shared_ptr< EmptyCell > Instance()
virtual std::shared_ptr< Track > DoFindTrack() override
std::vector< UIHandlePtr > HitTest(const TrackPanelMouseState &, const AudacityProject *) override
Subdivision Children(const wxRect &) override
void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass) override
LabeledChannelGroup(const std::shared_ptr< Track > &pTrack, wxCoord leftOffset)
Subdivision Children(const wxRect &rect) override
wxRect DrawingArea(TrackPanelDrawingContext &, const wxRect &rect, const wxRect &, unsigned iPass) override
Subdivision Children(const wxRect &rect) override
ResizingChannelGroup(const std::shared_ptr< Track > &pTrack, wxCoord leftOffset)
Subdivision Children(const wxRect &rect) override
Subdivision Children(const wxRect &rect) override
Subdivision Children(const wxRect &rect) override
VRulerAndChannel(const std::shared_ptr< ChannelView > &pView, wxCoord leftOffset)
void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass) override
VRulersAndChannels(const std::shared_ptr< Channel > &pChannel, ChannelView::Refinement refinement, wxCoord leftOffset)
Subdivision Children(const wxRect &rect) override