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// Draws the track name on the track, if it is needed.
1071void DrawTrackName(int leftOffset, TrackPanelDrawingContext &context,
1072 const Channel &channel, const wxRect & rect)
1073{
1074 if (!TrackArtist::Get(context)->mbShowTrackNameInTrack)
1075 return;
1076 const auto artist = TrackArtist::Get(context);
1077 const auto &pendingTracks = *artist->pPendingTracks;
1078 auto &track = pendingTracks.SubstitutePendingChangedTrack(GetTrack(channel));
1079 auto name = track.GetName();
1080 if (name.IsEmpty())
1081 return;
1082 auto &dc = context.dc;
1083 wxBrush Brush;
1084 wxCoord textWidth, textHeight;
1085 GetTrackNameExtent(dc, channel, &textWidth, &textHeight);
1086
1087 // Logic for name background translucency (aka 'shields')
1088 // Tracks less than kOpaqueHeight high will have opaque shields.
1089 // Tracks more than kTranslucentHeight will have maximum translucency for shields.
1090 const int kOpaqueHeight = 44;
1091 const int kTranslucentHeight = 124;
1092
1093 // PRL: to do: reexamine this strange use of ChannelView::GetHeight,
1094 // ultimately to compute an opacity
1095 int h = ChannelView::Get(channel).GetHeight();
1096
1097 // f codes the opacity as a number between 0.0 and 1.0
1098 float f = wxClip((h-kOpaqueHeight)/(float)(kTranslucentHeight-kOpaqueHeight),0.0,1.0);
1099 // kOpaque is the shield's alpha for tracks that are not tall
1100 // kTranslucent is the shield's alpha for tracks that are tall.
1101 const int kOpaque = 255;
1102 const int kTranslucent = 140;
1103 // 0.0 maps to full opacity, 1.0 maps to full translucency.
1104 int opacity = 255 - (255-140)*f;
1105
1106 const auto nameRect =
1107 GetTrackNameRect( leftOffset, rect, textWidth, textHeight );
1108
1109#ifdef __WXMAC__
1110 // Mac dc is a graphics dc already.
1111 AColor::UseThemeColour( &dc, clrTrackInfoSelected, clrTrackPanelText, opacity );
1112 dc.DrawRoundedRectangle( nameRect, 8.0 );
1113#else
1114 // This little dance with wxImage in order to draw to a graphic dc
1115 // which we can then paste as a translucent bitmap onto the real dc.
1116 enum : int {
1117 SecondMarginX = 1, SecondMarginY = 1,
1118 SecondMarginsX = 2 * SecondMarginX, SecondMarginsY = 2 * SecondMarginY,
1119 };
1120 wxImage image(
1121 textWidth + MarginsX + SecondMarginsX,
1122 textHeight + MarginsY + SecondMarginsY );
1123 image.InitAlpha();
1124 unsigned char *alpha=image.GetAlpha();
1125 memset(alpha, wxIMAGE_ALPHA_TRANSPARENT, image.GetWidth()*image.GetHeight());
1126
1127 {
1128 std::unique_ptr< wxGraphicsContext >
1129 pGc{ wxGraphicsContext::Create(image) };
1130 auto &gc = *pGc;
1131 // This is to a gc, not a dc.
1132 AColor::UseThemeColour( &gc, clrTrackInfoSelected, clrTrackPanelText, opacity );
1133 // Draw at 1,1, not at 0,0 to avoid clipping of the antialiasing.
1134 gc.DrawRoundedRectangle(
1135 SecondMarginX, SecondMarginY,
1136 textWidth + MarginsX, textHeight + MarginsY, 8.0 );
1137 // destructor of gc updates the wxImage.
1138 }
1139 wxBitmap bitmap( image );
1140 dc.DrawBitmap( bitmap,
1141 nameRect.x - SecondMarginX, nameRect.y - SecondMarginY );
1142#endif
1143 dc.SetTextForeground(theTheme.Colour( clrTrackPanelText ));
1144 dc.DrawText(track.GetName(),
1145 nameRect.x + MarginX,
1146 nameRect.y + MarginY);
1147}
1148
1149/*
1150
1151 The following classes define the subdivision of the area of the TrackPanel
1152 into cells with differing responses to mouse, keyboard, and scroll wheel
1153 events.
1154
1155 The classes defining the less inclusive areas are earlier, while those
1156 defining ever larger hierarchical groupings of cells are later.
1157
1158 To describe that subdivision again, informally, and top-down:
1159
1160 Firstly subtract margin areas, on the left and right, that do not interact.
1161
1162 Secondly subtract a noninterative margin above the first track, and an area
1163 below all tracks that causes deselection of all tracks if you click it.
1164 (One or both of those areas might be vertically scrolled off-screen, though.)
1165 Divide what remains into areas corresponding to the several tracks.
1166
1167 Thirdly, for each track, subtract an area below, which you can click and drag
1168 to resize the track vertically.
1169
1170 Fourthly, subtract an area at the left, which contains the track controls,
1171 such as the menu and delete and minimize buttons, and others appropriate
1172 to the track subtype.
1173
1174 Fifthly, divide what remains into the vertically stacked channels, if there
1175 are more than one, alternating with separators, which can be clicked to
1176 resize the channel views.
1177
1178 Sixthly, divide each channel into one or more vertically stacked sub-views.
1179
1180 Lastly, split the area for each sub-view into a vertical ruler, and an area
1181 that displays the channel's own contents.
1182
1183*/
1184
1186 std::vector< UIHandlePtr > HitTest(
1187 const TrackPanelMouseState &, const AudacityProject *) override
1188 { return {}; }
1189 virtual std::shared_ptr< Track > DoFindTrack() override { return {}; }
1190 static std::shared_ptr<EmptyCell> Instance()
1191 {
1192 static auto instance = std::make_shared< EmptyCell >();
1193 return instance;
1194 }
1195
1196 // TrackPanelDrawable implementation
1197 void Draw(
1198 TrackPanelDrawingContext &context,
1199 const wxRect &rect, unsigned iPass ) override
1200 {
1201 if ( iPass == TrackArtist::PassMargins ) {
1202 // Draw a margin area of TrackPanel
1203 auto dc = &context.dc;
1204
1205 AColor::TrackPanelBackground( dc, false );
1206 dc->DrawRectangle( rect );
1207 }
1208 }
1209};
1210
1211// A vertical ruler left of a channel
1214 const std::shared_ptr<ChannelView> &pView, wxCoord leftOffset)
1215 : mpView{ pView }, mLeftOffset{ leftOffset } {}
1216 Subdivision Children( const wxRect &rect ) override
1217 {
1218 return { Axis::X, Refinement{
1219 { rect.GetLeft(),
1220 ChannelVRulerControls::Get(*mpView).shared_from_this() },
1221 { mLeftOffset, mpView }
1222 } };
1223 }
1224 std::shared_ptr<ChannelView> mpView;
1226};
1227
1228// One or more sub-views of one channel, stacked vertically, each containing
1229// a vertical ruler and a channel
1232 const std::shared_ptr<Channel> &pChannel,
1233 ChannelView::Refinement refinement, wxCoord leftOffset)
1234 : mpChannel{ pChannel }
1235 , mRefinement{ std::move( refinement ) }
1236 , mLeftOffset{ leftOffset } {}
1237 Subdivision Children( const wxRect &rect ) override
1238 {
1239 Refinement refinement;
1240 auto y1 = rect.GetTop();
1241 for ( const auto &subView : mRefinement ) {
1242 y1 = std::max( y1, subView.first );
1243 refinement.emplace_back( y1,
1244 std::make_shared< VRulerAndChannel >(
1245 subView.second, mLeftOffset ) );
1246 }
1247 return { Axis::Y, std::move( refinement ) };
1248 }
1249
1250 // TrackPanelDrawable implementation
1251 void Draw(
1252 TrackPanelDrawingContext &context,
1253 const wxRect &rect, unsigned iPass ) override
1254 {
1255 // This overpaints the track area, but sometimes too the stereo channel
1256 // separator, so draw at least later than that
1257 if ( iPass == TrackArtist::PassBorders ) {
1258 DrawTrackName(mLeftOffset, context, *mpChannel, rect);
1259 }
1260 if ( iPass == TrackArtist::PassControls ) {
1261 if (mRefinement.size() > 1) {
1262 // Draw lines separating sub-views
1263 auto &dc = context.dc;
1264 AColor::CursorColor( &dc );
1265 auto iter = mRefinement.begin() + 1, end = mRefinement.end();
1266 for ( ; iter != end; ++iter ) {
1267 auto yy = iter->first;
1268 AColor::Line( dc, mLeftOffset, yy, rect.GetRight(), yy );
1269 }
1270 }
1271 }
1272 }
1273
1275 TrackPanelDrawingContext &context,
1276 const wxRect &rect, const wxRect &panelRect, unsigned iPass ) override
1277 {
1278 auto result = rect;
1279 if ( iPass == TrackArtist::PassBorders ) {
1280 if ( true ) {
1281 wxCoord textWidth, textHeight;
1282 GetTrackNameExtent(context.dc, *mpChannel, &textWidth, &textHeight);
1283 result =
1284 GetTrackNameRect( mLeftOffset, rect, textWidth, textHeight );
1285 }
1286 }
1287 return result;
1288 }
1289
1290 std::shared_ptr<Channel> mpChannel;
1293};
1294
1295//Simply fills area using specified brush and outlines borders
1297{
1298 //Required to keep selection behaviour similar to others
1299 std::shared_ptr<Channel> mpChannel;
1301public:
1306 const std::shared_ptr<Channel>& pChannel, int fillBrushName
1307 ) : mpChannel{ pChannel }, mFillBrushName{ fillBrushName }
1308 {
1309 }
1310
1312
1314 const wxRect& rect, unsigned iPass) override
1315 {
1316 if (iPass == TrackArtist::PassBackground)
1317 {
1318 context.dc.SetPen(*wxTRANSPARENT_PEN);
1319 AColor::UseThemeColour(&context.dc, mFillBrushName);
1320 context.dc.DrawRectangle(rect);
1321 wxRect bevel(rect.x, rect.y, rect.width - 1, rect.height - 1);
1322 AColor::BevelTrackInfo(context.dc, true, bevel, false);
1323 }
1324 }
1325
1326 std::shared_ptr<Track> DoFindTrack() override
1327 {
1328 return GetTrack(*mpChannel).shared_from_this();
1329 }
1330
1331 std::vector<UIHandlePtr> HitTest(const TrackPanelMouseState& state,
1332 const AudacityProject* pProject) override
1333 {
1334 return {};
1335 }
1336};
1337
1338//Simply place children one after another horizontally, without any specific logic
1340
1342
1344 : mRefinement(std::move(refinement))
1345 {
1346 }
1347
1348 Subdivision Children(const wxRect& /*rect*/) override
1349 {
1350 return { Axis::X, mRefinement };
1351 }
1352
1353};
1354
1355
1356// optional affordance area, and n channels with vertical rulers,
1357// alternating with n - 1 resizers;
1358// each channel-ruler pair might be divided into multiple views
1360 ChannelStack(const std::shared_ptr<Track> &pTrack, wxCoord leftOffset)
1361 : mpTrack{ pTrack }, mLeftOffset{ leftOffset } {}
1362 Subdivision Children(const wxRect &rect_) override
1363 {
1364 auto rect = rect_;
1365 Refinement refinement;
1366
1367 const auto channels = mpTrack->Channels();
1368 const auto pLast = *channels.rbegin();
1369 wxCoord yy = rect.GetTop();
1370 auto heights = FindAdjustedChannelHeights(*mpTrack);
1371 auto pHeight = heights.begin();
1372 for (auto pChannel : channels) {
1373 auto &view = ChannelView::Get(*pChannel);
1374 if (auto affordance = view.GetAffordanceControls()) {
1375 auto panelRect = std::make_shared<EmptyPanelRect>(pChannel,
1376 mpTrack->GetSelected()
1377 ? clrTrackInfoSelected : clrTrackInfo);
1378 Refinement hgroup {
1379 std::make_pair(rect.GetLeft() + 1, panelRect),
1380 std::make_pair(mLeftOffset, affordance)
1381 };
1382 refinement
1383 .emplace_back(yy, std::make_shared<HorizontalGroup>(hgroup));
1385 }
1386
1387 auto height = *pHeight++;
1388 rect.SetTop(yy);
1389 rect.SetHeight(height - kChannelSeparatorThickness);
1390 refinement.emplace_back(yy,
1391 std::make_shared<VRulersAndChannels>(pChannel,
1392 ChannelView::Get(*pChannel).GetSubViews(rect),
1393 mLeftOffset));
1394 if (pChannel != pLast) {
1395 yy += height;
1396 refinement.emplace_back(
1399 .shared_from_this());
1400 }
1401 }
1402
1403 return { Axis::Y, std::move(refinement) };
1404 }
1405
1407 const wxRect& rect, unsigned iPass) override
1408 {
1409 TrackPanelGroup::Draw(context, rect, iPass);
1410 if (iPass == TrackArtist::PassFocus && mpTrack->IsSelected()) {
1411 const auto channels = mpTrack->Channels();
1412 const auto pLast = *channels.rbegin();
1413 wxCoord yy = rect.GetTop();
1414 auto heights = FindAdjustedChannelHeights(*mpTrack);
1415 auto pHeight = heights.begin();
1416 for (auto pChannel : channels) {
1417 auto& view = ChannelView::Get(*pChannel);
1418 auto height = *pHeight++;
1419 if (auto affordance = view.GetAffordanceControls())
1420 height += kAffordancesAreaHeight;
1421 auto trackRect = wxRect(
1422 mLeftOffset,
1423 yy,
1424 rect.GetRight() - mLeftOffset,
1426 TrackArt::DrawCursor(context, trackRect, mpTrack.get());
1427 yy += height;
1428 }
1429 }
1430 }
1431
1432 const std::shared_ptr<Track> mpTrack;
1434};
1435
1436// A track control panel, left of n vertical rulers and n channels
1437// alternating with n - 1 resizers
1440 const std::shared_ptr<Track> &pTrack, wxCoord leftOffset)
1441 : mpTrack{ pTrack }, mLeftOffset{ leftOffset } {}
1442 Subdivision Children(const wxRect &rect) override
1443 { return { Axis::X, Refinement{
1444 { rect.GetLeft(),
1445 TrackControls::Get(*mpTrack).shared_from_this() },
1446 { rect.GetLeft() + kTrackInfoWidth,
1447 std::make_shared<ChannelStack>(mpTrack, mLeftOffset) }
1448 } }; }
1449
1450 // TrackPanelDrawable implementation
1452 const wxRect &rect, unsigned iPass) override
1453 {
1454 if (iPass == TrackArtist::PassBorders) {
1455 auto &dc = context.dc;
1456 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1457 dc.SetPen(*wxBLACK_PEN);
1458
1459 // border
1460 dc.DrawRectangle(
1461 rect.x, rect.y,
1462 rect.width - kShadowThickness, rect.height - kShadowThickness
1463 );
1464
1465 // shadow
1466 // Stroke lines along bottom and right, which are slightly short at
1467 // bottom-left and top-right
1468 const auto right = rect.GetRight();
1469 const auto bottom = rect.GetBottom();
1470
1471 // bottom
1472 AColor::Line(dc, rect.x + 2, bottom, right, bottom);
1473 // right
1474 AColor::Line(dc, right, rect.y + 2, right, bottom);
1475 }
1476 if (iPass == TrackArtist::PassFocus) {
1477 // Sometimes highlight is not drawn on backing bitmap. I thought
1478 // it was because FindFocus did not return the TrackPanel on Mac, but
1479 // when I removed that test, yielding this condition:
1480 // if (GetFocusedTrack() != NULL) {
1481 // the highlight was reportedly drawn even when something else
1482 // was the focus and no highlight should be drawn. -RBD
1483 const auto artist = TrackArtist::Get(context);
1484 auto &trackPanel = *artist->parent;
1485 auto &trackFocus = TrackFocus::Get( *trackPanel.GetProject() );
1486 if (trackFocus.Get() == mpTrack.get() &&
1487 wxWindow::FindFocus() == &trackPanel) {
1489 wxRect theRect = rect;
1490 auto &dc = context.dc;
1491 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1492
1493 AColor::TrackFocusPen(&dc, 2);
1494 dc.DrawRectangle(theRect);
1495 theRect.Deflate(1);
1496
1497 AColor::TrackFocusPen(&dc, 1);
1498 dc.DrawRectangle(theRect);
1499 theRect.Deflate(1);
1500
1501 AColor::TrackFocusPen(&dc, 0);
1502 dc.DrawRectangle(theRect);
1503 }
1504 }
1505 }
1506
1508 const wxRect &rect, const wxRect &, unsigned iPass) override
1509 {
1510 if (iPass == TrackArtist::PassBorders)
1511 return {
1512 rect.x - kBorderThickness,
1513 rect.y - kBorderThickness,
1514 rect.width + 2 * kBorderThickness + kShadowThickness,
1515 rect.height + 2 * kBorderThickness + kShadowThickness
1516 };
1517 else if (iPass == TrackArtist::PassFocus) {
1518 constexpr auto extra = kBorderThickness + 3;
1519 return {
1520 rect.x - extra,
1521 rect.y - extra,
1522 rect.width + 2 * extra + kShadowThickness,
1523 rect.height + 2 * extra + kShadowThickness
1524 };
1525 }
1526 else
1527 return rect;
1528 }
1529
1530 const std::shared_ptr<Track> mpTrack;
1532};
1533
1534// Stacks a label and a single or multi-channel track on a resizer below,
1535// which is associated with the last channel
1538 const std::shared_ptr<Track> &pTrack, wxCoord leftOffset)
1539 : mpTrack{ pTrack }, mLeftOffset{ leftOffset } {}
1540 Subdivision Children(const wxRect &rect) override
1541 { return { Axis::Y, Refinement{
1542 { rect.GetTop(),
1543 std::make_shared<LabeledChannelGroup>(mpTrack, mLeftOffset) },
1544 { rect.GetTop() + rect.GetHeight() - kTrackSeparatorThickness,
1546 **mpTrack->Channels().rbegin()).shared_from_this()
1547 }
1548 } }; }
1549 const std::shared_ptr<Track> mpTrack;
1551};
1552
1553// Stacks a dead area at top, the tracks, and the click-to-deselect area below
1555 explicit Subgroup(TrackPanel &panel) : mPanel{ panel } {}
1556 Subdivision Children(const wxRect &rect) override
1557 {
1558 const auto &viewInfo = *mPanel.GetViewInfo();
1559 wxCoord yy = -viewInfo.vpos;
1560 Refinement refinement;
1561
1562 auto &tracks = *mPanel.GetTracks();
1563 if (!tracks.empty())
1564 refinement.emplace_back( yy, EmptyCell::Instance() ),
1565 yy += kTopMargin;
1566
1567 for (const auto pTrack : tracks) {
1568 wxCoord height = 0;
1569 for (auto pChannel : pTrack->Channels()) {
1570 auto &view = ChannelView::Get(*pChannel);
1571 height += view.GetHeight();
1572 }
1573 refinement.emplace_back( yy,
1574 std::make_shared<ResizingChannelGroup>(
1575 pTrack->SharedPointer(), viewInfo.GetLeftOffset())
1576 );
1577 yy += height;
1578 }
1579
1580 refinement.emplace_back(std::max(0, yy), mPanel.GetBackgroundCell());
1581
1582 return { Axis::Y, std::move(refinement) };
1583 }
1585};
1586
1587// Main group shaves off the left and right margins
1589 explicit MainGroup(TrackPanel &panel) : mPanel{ panel } {}
1590 Subdivision Children(const wxRect &rect) override
1591 { return { Axis::X, Refinement{
1592 { 0, EmptyCell::Instance() },
1593 { kLeftMargin, std::make_shared<Subgroup>(mPanel) },
1594 { rect.GetRight() + 1 - kRightMargin, EmptyCell::Instance() }
1595 } }; }
1597};
1598
1599}
1600
1601std::shared_ptr<TrackPanelNode> TrackPanel::Root()
1602{
1603 // Root and other subgroup objects are throwaways.
1604 // They might instead be cached to avoid repeated allocation.
1605 // That cache would need invalidation when there is addition, deletion, or
1606 // permutation of tracks, or change of width of the vertical rulers.
1607 return std::make_shared< MainGroup >( *this );
1608}
1609
1610// This finds the rectangle of a given track (including all channels),
1611// either that of the label 'adornment' or the track itself
1612// The given track is assumed to be the first channel
1613wxRect TrackPanel::FindTrackRect( const Track * target )
1614{
1615 return CellularPanel::FindRect( [&] ( TrackPanelNode &node ) {
1616 if (auto pGroup = dynamic_cast<const LabeledChannelGroup*>( &node ))
1617 return pGroup->mpTrack.get() == target;
1618 return false;
1619 } );
1620}
1621
1623{
1624 auto rect = FindTrackRect(target);
1625 if (rect != wxRect{}) {
1626 // Enlarge horizontally.
1627 // PRL: perhaps it's one pixel too much each side, including some gray
1628 // beyond the yellow?
1629 rect.x = 0;
1630 GetClientSize(&rect.width, nullptr);
1631
1632 // Enlarge vertically, enough to enclose the yellow focus border pixels
1633 // The the outermost ring of gray pixels is included on three sides
1634 // but not the top (should that be fixed?)
1635
1636 // (Note that TrackPanel paints its focus over the "top margin" of the
1637 // rectangle allotted to the track, according to ChannelView::GetY() and
1638 // ChannelView::GetHeight(), but also over the margin of the next track.)
1639
1640 rect.height += kBottomMargin;
1641 int dy = kTopMargin - 1;
1642 rect.Inflate( 0, dy );
1643
1644 // Note that this rectangle does not coincide with any one of
1645 // the nodes in the subdivision.
1646 }
1647 return rect;
1648}
1649
1650std::vector<wxRect> TrackPanel::FindRulerRects(const Channel &target)
1651{
1652 std::vector<wxRect> results;
1653 VisitCells( [&](const wxRect &rect, TrackPanelCell &visited) {
1654 if (auto pRuler = dynamic_cast<const ChannelVRulerControls*>(&visited))
1655 if (auto pView = pRuler->GetChannelView())
1656 if (pView->FindChannel().get() == &target)
1657 results.push_back(rect);
1658 } );
1659 return results;
1660}
1661
1662std::shared_ptr<TrackPanelCell> TrackPanel::GetFocusedCell()
1663{
1664 auto pTrack = TrackFocus::Get(*GetProject()).Get();
1665 return pTrack
1666 ? ChannelView::Get(*pTrack->GetChannel(0)).shared_from_this()
1668}
1669
1671{
1672 // This may have a side-effect of assigning a focus if there was none
1673 auto& trackFocus = TrackFocus::Get(*GetProject());
1674 trackFocus.Set(trackFocus.Get());
1676}
1677
1679{
1680 if (message.focusPanel)
1681 SetFocus();
1682 if (auto cell = GetFocusedCell())
1683 Refresh(false);
1684}
wxImage(22, 22)
END_EVENT_TABLE()
const TranslatableString name
Definition: Distortion.cpp:76
XO("Cut/Copy/Paste")
#define safenew
Definition: MemoryX.h:9
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
THEME_API Theme theTheme
Definition: Theme.cpp:82
constexpr auto kTimerInterval
#define A(N)
Definition: ToChars.cpp:62
static Settings & settings()
Definition: TrackInfo.cpp:69
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:187
static void CursorColor(wxDC *dc)
Definition: AColor.cpp:451
static void TrackFocusPen(wxDC *dc, int level)
Definition: AColor.cpp:469
static void BevelTrackInfo(wxDC &dc, bool up, const wxRect &r, bool highlight=false)
Definition: AColor.cpp:340
static void TrackPanelBackground(wxDC *dc, bool selected)
Definition: AColor.cpp:446
static void UseThemeColour(wxDC *dc, int iBrush, int iPen=-1, int alpha=255)
Definition: AColor.cpp:368
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)
int GetHeight() const
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
wxColour & Colour(int iIndex)
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:208
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
void DrawTrackName(int leftOffset, TrackPanelDrawingContext &context, const Channel &channel, const wxRect &rect)
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