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( kTrackInfoBtnSize == 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{
827 if (evt.type == AudioIOEvent::MONITOR)
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::PassControls ) {
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, mLeftOffset, yy, rect.GetRight(), yy );
1188 }
1189 }
1190 }
1191 }
1192
1194 TrackPanelDrawingContext &context,
1195 const wxRect &rect, const wxRect &panelRect, unsigned iPass ) override
1196 {
1197 auto result = rect;
1198 if ( iPass == TrackArtist::PassBorders ) {
1199 if ( true ) {
1200 wxCoord textWidth, textHeight;
1201 GetTrackNameExtent(context.dc, *mpChannel, &textWidth, &textHeight);
1202 result =
1203 GetTrackNameRect( mLeftOffset, rect, textWidth, textHeight );
1204 }
1205 }
1206 return result;
1207 }
1208
1209 std::shared_ptr<Channel> mpChannel;
1212};
1213
1214//Simply fills area using specified brush and outlines borders
1216{
1217 //Required to keep selection behaviour similar to others
1218 std::shared_ptr<Channel> mpChannel;
1220public:
1225 const std::shared_ptr<Channel>& pChannel, int fillBrushName
1226 ) : mpChannel{ pChannel }, mFillBrushName{ fillBrushName }
1227 {
1228 }
1229
1231
1233 const wxRect& rect, unsigned iPass) override
1234 {
1235 if (iPass == TrackArtist::PassBackground)
1236 {
1237 context.dc.SetPen(*wxTRANSPARENT_PEN);
1238 AColor::UseThemeColour(&context.dc, mFillBrushName);
1239 context.dc.DrawRectangle(rect);
1240 wxRect bevel(rect.x, rect.y, rect.width - 1, rect.height - 1);
1241 AColor::BevelTrackInfo(context.dc, true, bevel, false);
1242 }
1243 }
1244
1245 std::shared_ptr<Track> DoFindTrack() override
1246 {
1247 return GetTrack(*mpChannel).shared_from_this();
1248 }
1249
1250 std::vector<UIHandlePtr> HitTest(const TrackPanelMouseState& state,
1251 const AudacityProject* pProject) override
1252 {
1253 return {};
1254 }
1255};
1256
1257//Simply place children one after another horizontally, without any specific logic
1259
1261
1263 : mRefinement(std::move(refinement))
1264 {
1265 }
1266
1267 Subdivision Children(const wxRect& /*rect*/) override
1268 {
1269 return { Axis::X, mRefinement };
1270 }
1271
1272};
1273
1274
1275// optional affordance area, and n channels with vertical rulers,
1276// alternating with n - 1 resizers;
1277// each channel-ruler pair might be divided into multiple views
1279 ChannelStack(const std::shared_ptr<Track> &pTrack, wxCoord leftOffset)
1280 : mpTrack{ pTrack }, mLeftOffset{ leftOffset } {}
1281 Subdivision Children(const wxRect &rect_) override
1282 {
1283 auto rect = rect_;
1284 Refinement refinement;
1285
1286 const auto channels = mpTrack->Channels();
1287 const auto pLast = *channels.rbegin();
1288 wxCoord yy = rect.GetTop();
1289 auto heights = FindAdjustedChannelHeights(*mpTrack);
1290 auto pHeight = heights.begin();
1291 for (auto pChannel : channels) {
1292 auto &view = ChannelView::Get(*pChannel);
1293 if (auto affordance = view.GetAffordanceControls()) {
1294 auto panelRect = std::make_shared<EmptyPanelRect>(pChannel,
1295 mpTrack->GetSelected()
1296 ? clrTrackInfoSelected : clrTrackInfo);
1297 Refinement hgroup {
1298 std::make_pair(rect.GetLeft() + 1, panelRect),
1299 std::make_pair(mLeftOffset, affordance)
1300 };
1301 refinement
1302 .emplace_back(yy, std::make_shared<HorizontalGroup>(hgroup));
1304 }
1305
1306 auto height = *pHeight++;
1307 rect.SetTop(yy);
1308 rect.SetHeight(height - kChannelSeparatorThickness);
1309 refinement.emplace_back(yy,
1310 std::make_shared<VRulersAndChannels>(pChannel,
1311 ChannelView::Get(*pChannel).GetSubViews(rect),
1312 mLeftOffset));
1313 if (pChannel != pLast) {
1314 yy += height;
1315 refinement.emplace_back(
1318 .shared_from_this());
1319 }
1320 }
1321
1322 return { Axis::Y, std::move(refinement) };
1323 }
1324
1326 const wxRect& rect, unsigned iPass) override
1327 {
1328 TrackPanelGroup::Draw(context, rect, iPass);
1329 if (iPass == TrackArtist::PassFocus && mpTrack->IsSelected()) {
1330 const auto channels = mpTrack->Channels();
1331 const auto pLast = *channels.rbegin();
1332 wxCoord yy = rect.GetTop();
1333 auto heights = FindAdjustedChannelHeights(*mpTrack);
1334 auto pHeight = heights.begin();
1335 for (auto pChannel : channels) {
1336 auto& view = ChannelView::Get(*pChannel);
1337 auto height = *pHeight++;
1338 if (auto affordance = view.GetAffordanceControls())
1339 height += kAffordancesAreaHeight;
1340 auto trackRect = wxRect(
1341 mLeftOffset,
1342 yy,
1343 rect.GetRight() - mLeftOffset,
1345 TrackArt::DrawCursor(context, trackRect, mpTrack.get());
1346 yy += height;
1347 }
1348 }
1349 }
1350
1351 const std::shared_ptr<Track> mpTrack;
1353};
1354
1355// A track control panel, left of n vertical rulers and n channels
1356// alternating with n - 1 resizers
1359 const std::shared_ptr<Track> &pTrack, wxCoord leftOffset)
1360 : mpTrack{ pTrack }, mLeftOffset{ leftOffset } {}
1361 Subdivision Children(const wxRect &rect) override
1362 { return { Axis::X, Refinement{
1363 { rect.GetLeft(),
1364 TrackControls::Get(*mpTrack).shared_from_this() },
1365 { rect.GetLeft() + kTrackInfoWidth,
1366 std::make_shared<ChannelStack>(mpTrack, mLeftOffset) }
1367 } }; }
1368
1369 // TrackPanelDrawable implementation
1371 const wxRect &rect, unsigned iPass) override
1372 {
1373 if (iPass == TrackArtist::PassBorders) {
1374 auto &dc = context.dc;
1375 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1376 dc.SetPen(*wxBLACK_PEN);
1377
1378 // border
1379 dc.DrawRectangle(
1380 rect.x, rect.y,
1381 rect.width - kShadowThickness, rect.height - kShadowThickness
1382 );
1383
1384 // shadow
1385 // Stroke lines along bottom and right, which are slightly short at
1386 // bottom-left and top-right
1387 const auto right = rect.GetRight();
1388 const auto bottom = rect.GetBottom();
1389
1390 // bottom
1391 AColor::Line(dc, rect.x + 2, bottom, right, bottom);
1392 // right
1393 AColor::Line(dc, right, rect.y + 2, right, bottom);
1394 }
1395 if (iPass == TrackArtist::PassFocus) {
1396 // Sometimes highlight is not drawn on backing bitmap. I thought
1397 // it was because FindFocus did not return the TrackPanel on Mac, but
1398 // when I removed that test, yielding this condition:
1399 // if (GetFocusedTrack() != NULL) {
1400 // the highlight was reportedly drawn even when something else
1401 // was the focus and no highlight should be drawn. -RBD
1402 const auto artist = TrackArtist::Get(context);
1403 auto &trackPanel = *artist->parent;
1404 auto &trackFocus = TrackFocus::Get( *trackPanel.GetProject() );
1405 if (trackFocus.Get() == mpTrack.get() &&
1406 wxWindow::FindFocus() == &trackPanel) {
1408 wxRect theRect = rect;
1409 auto &dc = context.dc;
1410 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1411
1412 AColor::TrackFocusPen(&dc, 2);
1413 dc.DrawRectangle(theRect);
1414 theRect.Deflate(1);
1415
1416 AColor::TrackFocusPen(&dc, 1);
1417 dc.DrawRectangle(theRect);
1418 theRect.Deflate(1);
1419
1420 AColor::TrackFocusPen(&dc, 0);
1421 dc.DrawRectangle(theRect);
1422 }
1423 }
1424 }
1425
1427 const wxRect &rect, const wxRect &, unsigned iPass) override
1428 {
1429 if (iPass == TrackArtist::PassBorders)
1430 return {
1431 rect.x - kBorderThickness,
1432 rect.y - kBorderThickness,
1433 rect.width + 2 * kBorderThickness + kShadowThickness,
1434 rect.height + 2 * kBorderThickness + kShadowThickness
1435 };
1436 else if (iPass == TrackArtist::PassFocus) {
1437 constexpr auto extra = kBorderThickness + 3;
1438 return {
1439 rect.x - extra,
1440 rect.y - extra,
1441 rect.width + 2 * extra + kShadowThickness,
1442 rect.height + 2 * extra + kShadowThickness
1443 };
1444 }
1445 else
1446 return rect;
1447 }
1448
1449 const std::shared_ptr<Track> mpTrack;
1451};
1452
1453// Stacks a label and a single or multi-channel track on a resizer below,
1454// which is associated with the last channel
1457 const std::shared_ptr<Track> &pTrack, wxCoord leftOffset)
1458 : mpTrack{ pTrack }, mLeftOffset{ leftOffset } {}
1459 Subdivision Children(const wxRect &rect) override
1460 { return { Axis::Y, Refinement{
1461 { rect.GetTop(),
1462 std::make_shared<LabeledChannelGroup>(mpTrack, mLeftOffset) },
1463 { rect.GetTop() + rect.GetHeight() - kTrackSeparatorThickness,
1465 **mpTrack->Channels().rbegin()).shared_from_this()
1466 }
1467 } }; }
1468 const std::shared_ptr<Track> mpTrack;
1470};
1471
1472// Stacks a dead area at top, the tracks, and the click-to-deselect area below
1474 explicit Subgroup(TrackPanel &panel) : mPanel{ panel } {}
1475 Subdivision Children(const wxRect &rect) override
1476 {
1477 const auto &viewInfo = *mPanel.GetViewInfo();
1478 wxCoord yy = -viewInfo.vpos;
1479 Refinement refinement;
1480
1481 auto &tracks = *mPanel.GetTracks();
1482 if (!tracks.empty())
1483 refinement.emplace_back( yy, EmptyCell::Instance() ),
1484 yy += kTopMargin;
1485
1486 for (const auto pTrack : tracks) {
1487 wxCoord height = 0;
1488 for (auto pChannel : pTrack->Channels()) {
1489 auto &view = ChannelView::Get(*pChannel);
1490 height += view.GetHeight();
1491 }
1492 refinement.emplace_back( yy,
1493 std::make_shared<ResizingChannelGroup>(
1494 pTrack->SharedPointer(), viewInfo.GetLeftOffset())
1495 );
1496 yy += height;
1497 }
1498
1499 refinement.emplace_back(std::max(0, yy), mPanel.GetBackgroundCell());
1500
1501 return { Axis::Y, std::move(refinement) };
1502 }
1504};
1505
1506// Main group shaves off the left and right margins
1508 explicit MainGroup(TrackPanel &panel) : mPanel{ panel } {}
1509 Subdivision Children(const wxRect &rect) override
1510 { return { Axis::X, Refinement{
1511 { 0, EmptyCell::Instance() },
1512 { kLeftMargin, std::make_shared<Subgroup>(mPanel) },
1513 { rect.GetRight() + 1 - kRightMargin, EmptyCell::Instance() }
1514 } }; }
1516};
1517
1518}
1519
1520std::shared_ptr<TrackPanelNode> TrackPanel::Root()
1521{
1522 // Root and other subgroup objects are throwaways.
1523 // They might instead be cached to avoid repeated allocation.
1524 // That cache would need invalidation when there is addition, deletion, or
1525 // permutation of tracks, or change of width of the vertical rulers.
1526 return std::make_shared< MainGroup >( *this );
1527}
1528
1529// This finds the rectangle of a given track (including all channels),
1530// either that of the label 'adornment' or the track itself
1531// The given track is assumed to be the first channel
1532wxRect TrackPanel::FindTrackRect( const Track * target )
1533{
1534 return CellularPanel::FindRect( [&] ( TrackPanelNode &node ) {
1535 if (auto pGroup = dynamic_cast<const LabeledChannelGroup*>( &node ))
1536 return pGroup->mpTrack.get() == target;
1537 return false;
1538 } );
1539}
1540
1542{
1543 auto rect = FindTrackRect(target);
1544 if (rect != wxRect{}) {
1545 // Enlarge horizontally.
1546 // PRL: perhaps it's one pixel too much each side, including some gray
1547 // beyond the yellow?
1548 rect.x = 0;
1549 GetClientSize(&rect.width, nullptr);
1550
1551 // Enlarge vertically, enough to enclose the yellow focus border pixels
1552 // The the outermost ring of gray pixels is included on three sides
1553 // but not the top (should that be fixed?)
1554
1555 // (Note that TrackPanel paints its focus over the "top margin" of the
1556 // rectangle allotted to the track, according to ChannelView::GetY() and
1557 // ChannelView::GetHeight(), but also over the margin of the next track.)
1558
1559 rect.height += kBottomMargin;
1560 int dy = kTopMargin - 1;
1561 rect.Inflate( 0, dy );
1562
1563 // Note that this rectangle does not coincide with any one of
1564 // the nodes in the subdivision.
1565 }
1566 return rect;
1567}
1568
1569std::vector<wxRect> TrackPanel::FindRulerRects(const Channel &target)
1570{
1571 std::vector<wxRect> results;
1572 VisitCells( [&](const wxRect &rect, TrackPanelCell &visited) {
1573 if (auto pRuler = dynamic_cast<const ChannelVRulerControls*>(&visited))
1574 if (auto pView = pRuler->GetChannelView())
1575 if (pView->FindChannel().get() == &target)
1576 results.push_back(rect);
1577 } );
1578 return results;
1579}
1580
1581std::shared_ptr<TrackPanelCell> TrackPanel::GetFocusedCell()
1582{
1583 auto pTrack = TrackFocus::Get(*GetProject()).Get();
1584 return pTrack
1585 ? ChannelView::Get(*pTrack->GetChannel(0)).shared_from_this()
1587}
1588
1590{
1591 // This may have a side-effect of assigning a focus if there was none
1592 auto& trackFocus = TrackFocus::Get(*GetProject());
1593 trackFocus.Set(trackFocus.Get());
1595}
1596
1598{
1599 if (message.focusPanel)
1600 SetFocus();
1601 if (auto cell = GetFocusedCell())
1602 Refresh(false);
1603}
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:47
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
@ kTrackInfoBtnSize
Definition: ViewInfo.h:96
@ kVerticalPadding
Definition: ViewInfo.h:92
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:185
static void CursorColor(wxDC *dc)
Definition: AColor.cpp:449
static void TrackFocusPen(wxDC *dc, int level)
Definition: AColor.cpp:467
static void BevelTrackInfo(wxDC &dc, bool up, const wxRect &r, bool highlight=false)
Definition: AColor.cpp:338
static void TrackPanelBackground(wxDC *dc, bool selected)
Definition: AColor.cpp:444
static void UseThemeColour(wxDC *dc, int iBrush, int iPen=-1, int alpha=255)
Definition: AColor.cpp:366
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:381
std::shared_ptr< ChannelType > GetChannel(size_t iChannel)
Retrieve a channel, cast to the given type.
Definition: Channel.h:320
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:69
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:221
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:215
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
void SetHeight(int height)
Definition: ViewInfo.h:202
void UpdateScrollbarsForTracks()
Definition: Viewport.cpp:250
void ShowTrack(const Track &track)
Definition: Viewport.cpp:456
void SetHorizontalThumb(double scrollto, bool doScroll=true)
Definition: Viewport.cpp:201
void HandleResize()
Definition: Viewport.cpp:389
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
EmptyPanelRect(const std::shared_ptr< Channel > &pChannel, int fillBrushName)
std::shared_ptr< Track > DoFindTrack() override
void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass) override
std::vector< UIHandlePtr > HitTest(const TrackPanelMouseState &state, const AudacityProject *pProject) override
void SetLabel(const TranslatableString &label)
void SetFocus(const WindowPlacement &focus)
Set the window that accepts keyboard input.
Definition: BasicUI.h:384
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:213
std::unique_ptr< WindowPlacement > FindFocus()
Find the window that is accepting keyboard input, if any.
Definition: BasicUI.h:375
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:752
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)
wxRect DrawingArea(TrackPanelDrawingContext &context, const wxRect &rect, const wxRect &panelRect, unsigned iPass) override
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