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 "Project.h"
56#include "ProjectAudioIO.h"
57#include "ProjectAudioManager.h"
58#include "ProjectHistory.h"
59#include "ProjectWindows.h"
60#include "ProjectSettings.h"
61#include "ProjectStatus.h"
62#include "ProjectTimeRuler.h"
63#include "ProjectWindow.h"
64#include "SyncLock.h"
65#include "Theme.h"
66#include "TrackArt.h"
68
69#include "UndoManager.h"
70#include "UIHandle.h"
71
72#include "AColor.h"
73#include "AllThemeResources.h"
74#include "AudioIO.h"
75#include "float_cast.h"
76
77#include "Prefs.h"
78#include "RefreshCode.h"
79#include "TrackArtist.h"
80#include "TrackPanelAx.h"
82#include "WaveTrack.h"
83
84#include "FrameStatistics.h"
85
89
90//This loads the appropriate set of cursors, depending on platform.
91#include "../images/Cursors.h"
92
93#include <algorithm>
94
95#include <wx/dc.h>
96#include <wx/dcclient.h>
97#include <wx/graphics.h>
98
100
101static_assert( kVerticalPadding == kTopMargin + kBottomMargin );
102static_assert( kTrackInfoBtnSize == kAffordancesAreaHeight, "Drag bar is misaligned with the menu button");
103
167// Is the distance between A and B less than D?
168template < class A, class B, class DIST > bool within(A a, B b, DIST d)
169{
170 return (a > b - d) && (a < b + d);
171}
172
173BEGIN_EVENT_TABLE(TrackPanel, CellularPanel)
174 EVT_MOUSE_EVENTS(TrackPanel::OnMouseEvent)
175 EVT_KEY_DOWN(TrackPanel::OnKeyDown)
176
177 EVT_PAINT(TrackPanel::OnPaint)
178
179 EVT_TIMER(wxID_ANY, TrackPanel::OnTimer)
180
181 EVT_SIZE(TrackPanel::OnSize)
182
184
185
187std::unique_ptr<wxCursor> MakeCursor( int WXUNUSED(CursorId), const char * const pXpm[36], int HotX, int HotY )
188{
189#define CURSORS_SIZE32
190#ifdef CURSORS_SIZE32
191 const int HotAdjust =0;
192#else
193 const int HotAdjust =8;
194#endif
195
196 wxImage Image = wxImage(wxBitmap(pXpm).ConvertToImage());
197 Image.SetMaskColour(255,0,0);
198 Image.SetMask();// Enable mask.
199
200 Image.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_X, HotX-HotAdjust );
201 Image.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_Y, HotY-HotAdjust );
202 return std::make_unique<wxCursor>( Image );
203}
204
205
206namespace{
207
208AttachedWindows::RegisteredFactory sKey{
209 []( AudacityProject &project ) -> wxWeakRef< wxWindow > {
211 auto &viewInfo = ViewInfo::Get( project );
212 auto &window = ProjectWindow::Get( project );
213 auto mainPage = window.GetTrackListWindow();
214 wxASSERT( mainPage ); // to justify safenew
215
216 auto &tracks = TrackList::Get( project );
217 auto result = safenew TrackPanel(mainPage,
218 window.NextWindowID(),
219 wxDefaultPosition,
220 wxDefaultSize,
221 tracks.shared_from_this(),
222 &viewInfo,
223 &project,
224 &ruler);
225 SetProjectPanel( project, *result );
226 return result;
227 }
228};
229
230}
231
233{
235}
236
238{
239 return Get( const_cast< AudacityProject & >( project ) );
240}
241
243{
244 auto *pPanel = GetAttachedWindows(project).Find<TrackPanel>( sKey );
245 if (pPanel) {
246 pPanel->wxWindow::Destroy();
248 }
249}
250
251// Don't warn us about using 'this' in the base member initializer list.
252#ifndef __WXGTK__ //Get rid if this pragma for gtk
253#pragma warning( disable: 4355 )
254#endif
255TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id,
256 const wxPoint & pos,
257 const wxSize & size,
258 const std::shared_ptr<TrackList> &tracks,
259 ViewInfo * viewInfo,
262 : CellularPanel(parent, id, pos, size, viewInfo,
263 wxWANTS_CHARS | wxNO_BORDER),
264 mListener( &ProjectWindow::Get( *project ) ),
265 mTracks(tracks),
266 mRuler(ruler),
267 mTrackArtist(nullptr),
268 mRefreshBacking(false)
269#ifndef __WXGTK__ //Get rid if this pragma for gtk
270#pragma warning( default: 4355 )
271#endif
272{
273 SetLayoutDirection(wxLayout_LeftToRight);
274 SetLabel(XO("Track Panel"));
275 SetName(XO("Track Panel"));
276 SetBackgroundStyle(wxBG_STYLE_PAINT);
277
278 {
279 auto pAx = std::make_unique <TrackPanelAx>( *project );
280 pAx->SetWindow( this );
281 wxWeakRef< TrackPanel > weakThis{ this };
282 pAx->SetFinder(
283 [weakThis]( const Track &track ) -> wxRect {
284 if (weakThis)
285 return weakThis->FindTrackRect( &track );
286 return {};
287 }
288 );
289 TrackFocus::Get( *GetProject() ).SetAccessible(
290 *this, std::move( pAx ) );
291 }
292
293 mTrackArtist = std::make_unique<TrackArtist>( this );
294
295 mTimeCount = 0;
296 mTimer.parent = this;
297 // Timer is started after the window is visible
298 ProjectWindow::Get( *GetProject() ).Bind(wxEVT_IDLE,
299 &TrackPanel::OnIdle, this);
300
301 // Register for tracklist updates
303 mTracks->Subscribe([this](const TrackListEvent &event){
304 switch (event.mType) {
305 case TrackListEvent::RESIZING:
306 case TrackListEvent::ADDITION:
307 OnTrackListResizing(event); break;
308 case TrackListEvent::DELETION:
309 OnTrackListDeletion(); break;
310 case TrackListEvent::TRACK_REQUEST_VISIBLE:
311 OnEnsureVisible(event); break;
312 default:
313 break;
314 }
315 });
316
317 auto theProject = GetProject();
320
322 .Subscribe(*this, &TrackPanel::OnTrackFocusChange);
323
326
329
331 .Subscribe([this](const RealtimeEffectManagerMessage& msg)
332 {
333 if (auto pTrack = dynamic_cast<Track *>(msg.group))
334 //update "effects" button
335 RefreshTrack(pTrack);
336 });
337
339 ProjectTimeRuler::Get(*theProject).GetRuler().Subscribe([this](auto mode) { Refresh(); });
340
341 UpdatePrefs();
342}
343
344
346{
347 mTimer.Stop();
348
349 // This can happen if a label is being edited and the user presses
350 // ALT+F4 or Command+Q
351 if (HasCapture())
352 ReleaseMouse();
353}
354
356{
357 // All vertical rulers must be recalculated since the minimum and maximum
358 // frequencies may have been changed.
360
361 Refresh();
362}
363
367{
368 auto window = GetParent();
369
370 while(window != nullptr)
371 {
372 if(const auto projectWindow = dynamic_cast<ProjectWindow*>(window))
373 return projectWindow->FindProject().get();
374
375 window = window->GetParent();
376 }
377 return nullptr;
378}
379
380void TrackPanel::OnSize( wxSizeEvent &evt )
381{
382 evt.Skip();
383 const auto &size = evt.GetSize();
384 mViewInfo->SetWidth( size.GetWidth() );
385 mViewInfo->SetHeight( size.GetHeight() );
386}
387
388void TrackPanel::OnIdle(wxIdleEvent& event)
389{
390 event.Skip();
391 // The window must be ready when the timer fires (#1401)
392 if (IsShownOnScreen())
393 {
394 mTimer.Start(std::chrono::milliseconds{kTimerInterval}.count(), FALSE);
395
396 // Timer is started, we don't need the event anymore
397 GetProjectFrame( *GetProject() ).Unbind(wxEVT_IDLE,
398 &TrackPanel::OnIdle, this);
399 }
400 else
401 {
402 // Get another idle event, wx only guarantees we get one
403 // event after "some other normal events occur"
404 event.RequestMore();
405 }
406}
407
409void TrackPanel::OnTimer(wxTimerEvent& )
410{
411 mTimeCount++;
412
413 AudacityProject *const p = GetProject();
414 auto &window = ProjectWindow::Get( *p );
415
416 auto &projectAudioIO = ProjectAudioIO::Get( *p );
417 auto gAudioIO = AudioIO::Get();
418
419 // Check whether we were playing or recording, but the stream has stopped.
420 if (projectAudioIO.GetAudioIOToken()>0 && !IsAudioActive())
421 {
422 //the stream may have been started up after this one finished (by some other project)
423 //in that case reset the buttons don't stop the stream
424 auto &projectAudioManager = ProjectAudioManager::Get( *p );
425 projectAudioManager.Stop(!gAudioIO->IsStreamActive());
426 }
427
428 // Next, check to see if we were playing or recording
429 // audio, but now Audio I/O is completely finished.
430 if (projectAudioIO.GetAudioIOToken()>0 &&
431 !gAudioIO->IsAudioTokenActive(projectAudioIO.GetAudioIOToken()))
432 {
433 projectAudioIO.SetAudioIOToken(0);
434 window.RedrawProject();
435 }
438 }
439
440 // Notify listeners for timer ticks
441 window.GetPlaybackScroller().OnTimer();
442
443 DrawOverlays(false);
444 mRuler->DrawOverlays(false);
445
446 if(IsAudioActive() && gAudioIO->GetNumCaptureChannels()) {
447
448 // Periodically update the display while recording
449
450 if ((mTimeCount % 5) == 0) {
451 // Must tell OnPaint() to recreate the backing bitmap
452 // since we've not done a full refresh.
453 mRefreshBacking = true;
454 Refresh( false );
455 }
456 }
457 if(mTimeCount > 1000)
458 mTimeCount = 0;
459}
460
462{
463 Refresh(false);
464}
465
467{
468 if (message.type == UndoRedoMessage::Reset) {
469 TrackFocus::Get( *GetProject() ).Set( nullptr );
470 Refresh( false );
471 }
472}
473
476void TrackPanel::OnPaint(wxPaintEvent & /* event */)
477{
479
480 auto sw =
482
483 {
484 wxPaintDC dc(this);
485
486 // Retrieve the damage rectangle
487 wxRect box = GetUpdateRegion().GetBox();
488
489 // Recreate the backing bitmap if we have a full refresh
490 // (See TrackPanel::Refresh())
491 if (mRefreshBacking || (box == GetRect()))
492 {
493 // Reset (should a mutex be used???)
494 mRefreshBacking = false;
495
496 // Redraw the backing bitmap
498
499 // Copy it to the display
500 DisplayBitmap(dc);
501 }
502 else
503 {
504 // Copy full, possibly clipped, damage rectangle
505 RepairBitmap(dc, box.x, box.y, box.width, box.height);
506 }
507
508 // Done with the clipped DC
509
510 // Drawing now goes directly to the client area.
511 // DrawOverlays() may need to draw outside the clipped region.
512 // (Used to make a NEW, separate wxClientDC, but that risks flashing
513 // problems on Mac.)
514 dc.DestroyClippingRegion();
515 DrawOverlays(true, &dc);
516 }
517}
518
520{
522}
523
524namespace {
525 std::shared_ptr<Track> FindTrack(TrackPanelCell *pCell )
526 {
527 if (pCell)
528 return static_cast<CommonTrackPanelCell*>( pCell )->FindTrack();
529 return {};
530 }
531}
532
534 (TrackPanelCell *pClickedCell, TrackPanelCell *pLatestCell,
535 UIHandle::Result refreshResult)
536{
537 const auto panel = this;
538 auto pLatestTrack = FindTrack( pLatestCell ).get();
539
540 // This precaution checks that the track is not only nonnull, but also
541 // really owned by the track list
542 auto pClickedTrack = GetTracks()->Lock(
543 std::weak_ptr<Track>{ FindTrack( pClickedCell ) }
544 ).get();
545
546 // TODO: make a finer distinction between refreshing the track control area,
547 // and the waveform area. As it is, redraw both whenever you must redraw either.
548
549 // Copy data from the underlying tracks to the pending tracks that are
550 // really displayed
551 TrackList::Get( *panel->GetProject() ).UpdatePendingTracks();
552
553 using namespace RefreshCode;
554
555 if (refreshResult & DestroyedCell) {
556 panel->UpdateViewIfNoTracks();
557 // Beware stale pointer!
558 if (pLatestTrack == pClickedTrack)
559 pLatestTrack = NULL;
560 pClickedTrack = NULL;
561 }
562
563 if (pClickedTrack && (refreshResult & RefreshCode::UpdateVRuler))
564 panel->UpdateVRuler(pClickedTrack);
565
566 if (refreshResult & RefreshCode::DrawOverlays) {
567 panel->DrawOverlays(false);
568 mRuler->DrawOverlays(false);
569 }
570
571 // Refresh all if told to do so, or if told to refresh a track that
572 // is not known.
573 const bool refreshAll =
574 ( (refreshResult & RefreshAll)
575 || ((refreshResult & RefreshCell) && !pClickedTrack)
576 || ((refreshResult & RefreshLatestCell) && !pLatestTrack));
577
578 if (refreshAll)
579 panel->Refresh(false);
580 else {
581 if (refreshResult & RefreshCell)
582 panel->RefreshTrack(pClickedTrack);
583 if (refreshResult & RefreshLatestCell)
584 panel->RefreshTrack(pLatestTrack);
585 }
586
587 if (refreshResult & FixScrollbars)
588 panel->MakeParentRedrawScrollbars();
589
590 if (refreshResult & Resize)
591 panel->GetListener()->TP_HandleResize();
592
593 if ((refreshResult & RefreshCode::EnsureVisible) && pClickedTrack) {
594 TrackFocus::Get(*GetProject()).Set(pClickedTrack);
595 pClickedTrack->EnsureVisible();
596 }
597}
598
600{
602}
603
605{
607}
608
610{
612 return ProjectAudioIO::Get( *p ).IsAudioActive();
613}
614
616{
617 auto status = st;
618 if (HasEscape())
619 /* i18n-hint Esc is a key on the keyboard */
620 status.Join( XO("(Esc to cancel)"), " " );
621 ProjectStatus::Get( *GetProject() ).Set( status );
622}
623
625{
626 // Full refresh since the label area may need to indicate
627 // newly selected tracks.
628 Refresh(false);
629
630 // Make sure the ruler follows suit.
632}
633
634// Counts selected tracks, counting stereo tracks as one track.
636{
637 return GetTracks()->Selected().size();
638}
639
641{
642 if (mTracks->empty())
643 {
644 // BG: There are no more tracks on screen
645 //BG: Set zoom to normal
647
648 //STM: Set selection to 0,0
649 //PRL: and default the rest of the selection information
651
652 // PRL: Following causes the time ruler to align 0 with left edge.
653 // Bug 972
654 mViewInfo->h = 0;
655
657 //STM: Clear message if all tracks are removed
659 }
660}
661
662// The tracks positions within the list have changed, so update the vertical
663// ruler size for the track that triggered the event.
665{
666 auto t = e.mpTrack.lock();
667 // A deleted track can trigger the event. In which case do nothing here.
668 // A deleted track can have a valid pointer but no owner, bug 2060
669 if( t && t->HasOwner() )
670 UpdateVRuler(t.get());
671
672 // fix for bug 2477
674}
675
676// Tracks have been removed from the list.
678{
679 // copy shared_ptr for safety, as in HandleClick
680 auto handle = Target();
681 if (handle) {
682 handle->OnProjectChange(GetProject());
683 }
684
685 // If the focused track disappeared but there are still other tracks,
686 // this reassigns focus.
687 TrackFocus( *GetProject() ).Get();
688
690}
691
692void TrackPanel::OnKeyDown(wxKeyEvent & event)
693{
694 switch (event.GetKeyCode())
695 {
696 // Allow PageUp and PageDown keys to
697 //scroll the Track Panel left and right
698 case WXK_PAGEUP:
700 return;
701
702 case WXK_PAGEDOWN:
704 return;
705
706 default:
707 // fall through to base class handler
708 event.Skip();
709 }
710}
711
712void TrackPanel::OnMouseEvent(wxMouseEvent & event)
713{
714 if (event.LeftDown()) {
715 // wxTimers seem to be a little unreliable, so this
716 // "primes" it to make sure it keeps going for a while...
717
718 // When this timer fires, we call TrackPanel::OnTimer and
719 // possibly update the screen for offscreen scrolling.
720 mTimer.Stop();
721 mTimer.Start(std::chrono::milliseconds{kTimerInterval}.count(), FALSE);
722 }
723
724
725 if (event.ButtonUp()) {
726 //EnsureVisible should be called after processing the up-click.
727 this->CallAfter( [this, event]{
728 const auto foundCell = FindCell(event.m_x, event.m_y);
729 const auto t = FindTrack( foundCell.pCell.get() );
730 if ( t ) {
731 TrackFocus::Get(*GetProject()).Set(t.get());
732 t->EnsureVisible();
733 }
734 } );
735 }
736
737 // Must also fall through to base class handler
738 event.Skip();
739}
740
742{
745}
746
747void TrackPanel::RefreshTrack(Track *trk, bool refreshbacking)
748{
749 if (!trk)
750 return;
751
752 // Always move to the first channel of the group, and use only
753 // the sum of channel heights, not the height of any channel alone!
754 trk = *GetTracks()->Find(trk);
755 auto height = ChannelView::GetChannelGroupHeight(trk);
756
757 // Set rectangle top according to the scrolling position, `vpos`
758 // Subtract the inset (above) and shadow (below) from the height of the
759 // rectangle, but not the border
760 // This matters because some separators do paint over the border
761 auto &view = ChannelView::Get(*trk->GetChannel(0));
762 const auto top =
763 -mViewInfo->vpos + view.GetCumulativeHeightBefore() + kTopInset;
764 height -= (kTopInset + kShadowThickness);
765
766 // Width also subtracts insets (left and right) plus shadow (right)
767 const auto left = kLeftInset;
768 const auto width = GetRect().GetWidth()
770
771 wxRect rect(left, top, width, height);
772
773 if( refreshbacking )
774 mRefreshBacking = true;
775
776 Refresh( false, &rect );
777}
778
779
784void TrackPanel::Refresh(bool eraseBackground /* = TRUE */,
785 const wxRect *rect /* = NULL */)
786{
787 // Tell OnPaint() to refresh the backing bitmap.
788 //
789 // Originally I had the check within the OnPaint() routine and it
790 // was working fine. That was until I found that, even though a full
791 // refresh was requested, Windows only set the onscreen portion of a
792 // window as damaged.
793 //
794 // So, if any part of the trackpanel was off the screen, full refreshes
795 // didn't work and the display got corrupted.
796 if( !rect || ( *rect == GetRect() ) )
797 {
798 mRefreshBacking = true;
799 }
800 wxWindow::Refresh(eraseBackground, rect);
801
802 CallAfter([this]{
803 if (GetProject())
805}
806
808{
809 if (evt.type == AudioIOEvent::MONITOR)
810 return;
811 // Some hit tests want to change their cursor to and from the ban symbol
813}
814
816
821{
822 wxRegion region = GetUpdateRegion();
823
824 const wxRect clip = GetRect();
825
827 mTrackArtist->pSelectedRegion = &sr;
828 mTrackArtist->pZoomInfo = mViewInfo;
830 *dc, Target(), mLastMouseState, mTrackArtist.get()
831 };
832
833 // Don't draw a bottom margin here.
834
835 const auto &settings = ProjectSettings::Get( *GetProject() );
836 bool bMultiToolDown =
837 (ToolCodes::multiTool == settings.GetTool());
838 bool envelopeFlag =
839 bMultiToolDown || (ToolCodes::envelopeTool == settings.GetTool());
840 bool bigPointsFlag =
841 bMultiToolDown || (ToolCodes::drawTool == settings.GetTool());
842 bool sliderFlag = bMultiToolDown;
843 bool brushFlag = false;
844#ifdef EXPERIMENTAL_BRUSH_TOOL
845 brushFlag = (ToolCodes::brushTool == settings.GetTool());
846#endif
847
848 const bool hasSolo = GetTracks()->Any<PlayableTrack>()
849 .any_of( [](const PlayableTrack *pt) {
850 pt = static_cast<const PlayableTrack *>(
852 return (pt && pt->GetSolo());
853 } );
854
855 mTrackArtist->drawEnvelope = envelopeFlag;
856 mTrackArtist->bigPoints = bigPointsFlag;
857 mTrackArtist->drawSliders = sliderFlag;
858 mTrackArtist->onBrushTool = brushFlag;
859 mTrackArtist->hasSolo = hasSolo;
860
862}
863
865(const std::shared_ptr< CommonTrackPanelCell > &pCell)
866{
867 mpBackground = pCell;
868}
869
870std::shared_ptr< CommonTrackPanelCell > TrackPanel::GetBackgroundCell()
871{
872 return mpBackground;
873}
874
875namespace {
880{
881 assert(t.IsLeader());
882 auto channels = t.Channels();
883 assert(!channels.empty());
884
885 // Collect heights, and count affordances
886 int nAffordances = 0;
887 int totalHeight = 0;
888 std::vector<int> oldHeights;
889 for (auto pChannel : channels) {
890 auto &view = ChannelView::Get(*pChannel);
891 const auto height = view.GetHeight();
892 totalHeight += height;
893 oldHeights.push_back(height);
894 if (view.GetAffordanceControls())
895 ++nAffordances;
896 }
897
898 // Allocate results
899 auto nChannels = static_cast<int>(oldHeights.size());
900 std::vector<int> results;
901 results.reserve(nChannels);
902
903 // Now reallocate the channel heights for the presence of affordances
904 // and separators
905 auto availableHeight = totalHeight
906 - nAffordances * kAffordancesAreaHeight
907 - (nChannels - 1) * kChannelSeparatorThickness
909 int cumulativeOldHeight = 0;
910 int cumulativeNewHeight = 0;
911 for (const auto &oldHeight : oldHeights) {
912 // Preserve the porportions among the stored heights
913 cumulativeOldHeight += oldHeight;
914 const auto newHeight =
915 cumulativeOldHeight * availableHeight / totalHeight
916 - cumulativeNewHeight;
917 cumulativeNewHeight += newHeight;
918 results.push_back(newHeight);
919 }
920
921 return results;
922}
923}
924
926{
927 for (auto t : GetTracks()->Any<WaveTrack>())
929
931}
932
934{
935 if (t)
937
939}
940
942{
943 assert(t.IsLeader());
944
945 auto heights = FindAdjustedChannelHeights(t);
946
947 wxRect rect(mViewInfo->GetVRulerOffset(),
948 0,
950 0);
951
952 auto pHeight = heights.begin();
953 for (auto pChannel : t.Channels()) {
954 auto &view = ChannelView::Get(*pChannel);
955 const auto height = *pHeight++;
956 rect.SetHeight(height);
957 const auto subViews = view.GetSubViews(rect);
958 if (subViews.empty())
959 continue;
960
961 auto iter = subViews.begin(), end = subViews.end(), next = iter;
962 auto yy = iter->first;
963 wxSize vRulerSize{ 0, 0 };
964 auto &size = view.vrulerSize;
965 for (; iter != end; iter = next) {
966 ++next;
967 auto nextY = (next == end)
968 ? height
969 : next->first;
970 rect.SetHeight(nextY - yy);
971 // This causes ruler size in the track to be reassigned:
972 ChannelVRulerControls::Get(*iter->second).UpdateRuler(rect);
973 // But we want to know the maximum width and height over all sub-views:
974 vRulerSize.IncTo({ size.first, size.second });
975 yy = nextY;
976 }
977 size = { vRulerSize.x, vRulerSize.y };
978 }
979}
980
982{
983 auto trackRange = GetTracks()->Any();
984 if (trackRange) {
985 wxSize s{ 0, 0 };
986 // Find maximum width over all channels
987 for (auto t : trackRange)
988 for (auto pChannel : t->Channels()) {
989 auto &size = ChannelView::Get(*pChannel).vrulerSize;
990 s.IncTo({ size.first, size.second });
991 }
992
993 if (mViewInfo->GetVRulerWidth() != s.GetWidth()) {
994 mViewInfo->SetVRulerWidth(s.GetWidth());
996 mViewInfo->GetLeftOffset()); // bevel on AdornedRuler
997 mRuler->Refresh();
998 }
999 }
1000 Refresh(false);
1001}
1002
1004{
1006 t ? &ChannelView::Get(*t->GetChannel(0)) : nullptr);
1007}
1008
1009// Tracks have been removed from the list.
1011{
1012 bool modifyState = e.mExtra;
1013 auto pTrack = e.mpTrack.lock();
1014 auto t = pTrack.get();
1015 // Promised by TrackListEvent for this event type:
1016 assert(!t || t->IsLeader());
1017 int trackTop = 0;
1018 int trackHeight =0;
1019 for (auto it : *GetTracks()) {
1020 trackTop += trackHeight;
1021 trackHeight = ChannelView::GetChannelGroupHeight(it);
1022
1023 if (it == t) {
1024 //We have found the track we want to ensure is visible.
1025
1026 //Get the size of the trackpanel.
1027 int width, height;
1028 GetSize(&width, &height);
1029
1030 if (trackTop < mViewInfo->vpos) {
1031 height = mViewInfo->vpos - trackTop + mViewInfo->scrollStep;
1032 height /= mViewInfo->scrollStep;
1033 mListener->TP_ScrollUpDown(-height);
1034 }
1035 else if (trackTop + trackHeight > mViewInfo->vpos + height) {
1036 height = (trackTop + trackHeight) - (mViewInfo->vpos + height);
1037 height = (height + mViewInfo->scrollStep + 1) / mViewInfo->scrollStep;
1038 mListener->TP_ScrollUpDown(height);
1039 }
1040
1041 break;
1042 }
1043 }
1044 Refresh(false);
1045
1046 if (modifyState)
1048}
1049
1050// 0.0 scrolls to top
1051// 1.0 scrolls to bottom.
1052void TrackPanel::VerticalScroll( float fracPosition){
1053
1054 int trackTop = 0;
1055 int trackHeight = 0;
1056
1057 auto tracks = GetTracks();
1058
1059 auto range = tracks->Any();
1060 if (!range.empty()) {
1061 trackHeight = ChannelView::GetChannelGroupHeight(*range.rbegin());
1062 --range.second;
1063 }
1064 trackTop = range.sum(ChannelView::GetChannelGroupHeight);
1065
1066 int delta;
1067
1068 //Get the size of the trackpanel.
1069 int width, height;
1070 GetSize(&width, &height);
1071
1072 delta = (fracPosition * (trackTop + trackHeight - height)) - mViewInfo->vpos + mViewInfo->scrollStep;
1073 //wxLogDebug( "Scroll down by %i pixels", delta );
1074 delta /= mViewInfo->scrollStep;
1075 mListener->TP_ScrollUpDown(delta);
1076 Refresh(false);
1077}
1078
1079
1080namespace {
1081 // Drawing constants
1082 // DisplaceX and MarginX are large enough to avoid overwriting <- symbol
1083 // See TrackArt::DrawNegativeOffsetTrackArrows
1084 enum : int {
1085 // Displacement of the rectangle from upper left corner
1087 // Size of margins about the text extent that determine the rectangle size
1089 // Derived constants
1091 };
1092
1094{
1095 // It is assumed that all channels we ever see are in groups that are
1096 // also Tracks
1097 return static_cast<Track &>(channel.GetChannelGroup());
1098}
1099
1100const Track &GetTrack(const Channel &channel)
1101{
1102 // It is assumed that all channels we ever see are in groups that are
1103 // also Tracks
1104 return static_cast<const Track &>(channel.GetChannelGroup());
1105}
1106
1108 wxDC &dc, const Channel &channel, wxCoord *pW, wxCoord *pH)
1109{
1110 wxFont labelFont(12, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1111 dc.SetFont(labelFont);
1112 dc.GetTextExtent(GetTrack(channel).GetName(), pW, pH);
1113}
1114
1116 int leftOffset,
1117 const wxRect &trackRect, wxCoord textWidth, wxCoord textHeight )
1118{
1119 return {
1120 leftOffset + DisplaceX,
1121 trackRect.y + DisplaceY,
1122 textWidth + MarginsX,
1123 textHeight + MarginsY
1124 };
1125}
1126
1127// Draws the track name on the track, if it is needed.
1128void DrawTrackName(int leftOffset, TrackPanelDrawingContext &context,
1129 const Channel &channel, const wxRect & rect)
1130{
1131 if (!TrackArtist::Get(context)->mbShowTrackNameInTrack)
1132 return;
1133 auto &track = *GetTrack(channel).SubstitutePendingChangedTrack();
1134 auto name = track.GetName();
1135 if (name.IsEmpty())
1136 return;
1137 if (!track.IsLeader())
1138 return;
1139 auto &dc = context.dc;
1140 wxBrush Brush;
1141 wxCoord textWidth, textHeight;
1142 GetTrackNameExtent(dc, channel, &textWidth, &textHeight);
1143
1144 // Logic for name background translucency (aka 'shields')
1145 // Tracks less than kOpaqueHeight high will have opaque shields.
1146 // Tracks more than kTranslucentHeight will have maximum translucency for shields.
1147 const int kOpaqueHeight = 44;
1148 const int kTranslucentHeight = 124;
1149
1150 // PRL: to do: reexamine this strange use of ChannelView::GetHeight,
1151 // ultimately to compute an opacity
1152 int h = ChannelView::Get(channel).GetHeight();
1153
1154 // f codes the opacity as a number between 0.0 and 1.0
1155 float f = wxClip((h-kOpaqueHeight)/(float)(kTranslucentHeight-kOpaqueHeight),0.0,1.0);
1156 // kOpaque is the shield's alpha for tracks that are not tall
1157 // kTranslucent is the shield's alpha for tracks that are tall.
1158 const int kOpaque = 255;
1159 const int kTranslucent = 140;
1160 // 0.0 maps to full opacity, 1.0 maps to full translucency.
1161 int opacity = 255 - (255-140)*f;
1162
1163 const auto nameRect =
1164 GetTrackNameRect( leftOffset, rect, textWidth, textHeight );
1165
1166#ifdef __WXMAC__
1167 // Mac dc is a graphics dc already.
1168 AColor::UseThemeColour( &dc, clrTrackInfoSelected, clrTrackPanelText, opacity );
1169 dc.DrawRoundedRectangle( nameRect, 8.0 );
1170#else
1171 // This little dance with wxImage in order to draw to a graphic dc
1172 // which we can then paste as a translucent bitmap onto the real dc.
1173 enum : int {
1174 SecondMarginX = 1, SecondMarginY = 1,
1175 SecondMarginsX = 2 * SecondMarginX, SecondMarginsY = 2 * SecondMarginY,
1176 };
1177 wxImage image(
1178 textWidth + MarginsX + SecondMarginsX,
1179 textHeight + MarginsY + SecondMarginsY );
1180 image.InitAlpha();
1181 unsigned char *alpha=image.GetAlpha();
1182 memset(alpha, wxIMAGE_ALPHA_TRANSPARENT, image.GetWidth()*image.GetHeight());
1183
1184 {
1185 std::unique_ptr< wxGraphicsContext >
1186 pGc{ wxGraphicsContext::Create(image) };
1187 auto &gc = *pGc;
1188 // This is to a gc, not a dc.
1189 AColor::UseThemeColour( &gc, clrTrackInfoSelected, clrTrackPanelText, opacity );
1190 // Draw at 1,1, not at 0,0 to avoid clipping of the antialiasing.
1191 gc.DrawRoundedRectangle(
1192 SecondMarginX, SecondMarginY,
1193 textWidth + MarginsX, textHeight + MarginsY, 8.0 );
1194 // destructor of gc updates the wxImage.
1195 }
1196 wxBitmap bitmap( image );
1197 dc.DrawBitmap( bitmap,
1198 nameRect.x - SecondMarginX, nameRect.y - SecondMarginY );
1199#endif
1200 dc.SetTextForeground(theTheme.Colour( clrTrackPanelText ));
1201 dc.DrawText(track.GetName(),
1202 nameRect.x + MarginX,
1203 nameRect.y + MarginY);
1204}
1205
1206/*
1207
1208 The following classes define the subdivision of the area of the TrackPanel
1209 into cells with differing responses to mouse, keyboard, and scroll wheel
1210 events.
1211
1212 The classes defining the less inclusive areas are earlier, while those
1213 defining ever larger hierarchical groupings of cells are later.
1214
1215 To describe that subdivision again, informally, and top-down:
1216
1217 Firstly subtract margin areas, on the left and right, that do not interact.
1218
1219 Secondly subtract a noninterative margin above the first track, and an area
1220 below all tracks that causes deselection of all tracks if you click it.
1221 (One or both of those areas might be vertically scrolled off-screen, though.)
1222 Divide what remains into areas corresponding to the several tracks.
1223
1224 Thirdly, for each track, subtract an area below, which you can click and drag
1225 to resize the track vertically.
1226
1227 Fourthly, subtract an area at the left, which contains the track controls,
1228 such as the menu and delete and minimize buttons, and others appropriate
1229 to the track subtype.
1230
1231 Fifthly, divide what remains into the vertically stacked channels, if there
1232 are more than one, alternating with separators, which can be clicked to
1233 resize the channel views.
1234
1235 Sixthly, divide each channel into one or more vertically stacked sub-views.
1236
1237 Lastly, split the area for each sub-view into a vertical ruler, and an area
1238 that displays the channel's own contents.
1239
1240*/
1241
1243 std::vector< UIHandlePtr > HitTest(
1244 const TrackPanelMouseState &, const AudacityProject *) override
1245 { return {}; }
1246 virtual std::shared_ptr< Track > DoFindTrack() override { return {}; }
1247 static std::shared_ptr<EmptyCell> Instance()
1248 {
1249 static auto instance = std::make_shared< EmptyCell >();
1250 return instance;
1251 }
1252
1253 // TrackPanelDrawable implementation
1254 void Draw(
1255 TrackPanelDrawingContext &context,
1256 const wxRect &rect, unsigned iPass ) override
1257 {
1258 if ( iPass == TrackArtist::PassMargins ) {
1259 // Draw a margin area of TrackPanel
1260 auto dc = &context.dc;
1261
1262 AColor::TrackPanelBackground( dc, false );
1263 dc->DrawRectangle( rect );
1264 }
1265 }
1266};
1267
1268// A vertical ruler left of a channel
1271 const std::shared_ptr<ChannelView> &pView, wxCoord leftOffset)
1272 : mpView{ pView }, mLeftOffset{ leftOffset } {}
1273 Subdivision Children( const wxRect &rect ) override
1274 {
1275 return { Axis::X, Refinement{
1276 { rect.GetLeft(),
1277 ChannelVRulerControls::Get(*mpView).shared_from_this() },
1278 { mLeftOffset, mpView }
1279 } };
1280 }
1281 std::shared_ptr<ChannelView> mpView;
1283};
1284
1285// One or more sub-views of one channel, stacked vertically, each containing
1286// a vertical ruler and a channel
1289 const std::shared_ptr<Channel> &pChannel,
1290 ChannelView::Refinement refinement, wxCoord leftOffset)
1291 : mpChannel{ pChannel }
1292 , mRefinement{ std::move( refinement ) }
1293 , mLeftOffset{ leftOffset } {}
1294 Subdivision Children( const wxRect &rect ) override
1295 {
1296 Refinement refinement;
1297 auto y1 = rect.GetTop();
1298 for ( const auto &subView : mRefinement ) {
1299 y1 = std::max( y1, subView.first );
1300 refinement.emplace_back( y1,
1301 std::make_shared< VRulerAndChannel >(
1302 subView.second, mLeftOffset ) );
1303 }
1304 return { Axis::Y, std::move( refinement ) };
1305 }
1306
1307 // TrackPanelDrawable implementation
1308 void Draw(
1309 TrackPanelDrawingContext &context,
1310 const wxRect &rect, unsigned iPass ) override
1311 {
1312 // This overpaints the track area, but sometimes too the stereo channel
1313 // separator, so draw at least later than that
1314 if ( iPass == TrackArtist::PassBorders ) {
1315 DrawTrackName(mLeftOffset, context, *mpChannel, rect);
1316 }
1317 if ( iPass == TrackArtist::PassControls ) {
1318 if (mRefinement.size() > 1) {
1319 // Draw lines separating sub-views
1320 auto &dc = context.dc;
1321 AColor::CursorColor( &dc );
1322 auto iter = mRefinement.begin() + 1, end = mRefinement.end();
1323 for ( ; iter != end; ++iter ) {
1324 auto yy = iter->first;
1325 AColor::Line( dc, mLeftOffset, yy, rect.GetRight(), yy );
1326 }
1327 }
1328 }
1329 }
1330
1332 TrackPanelDrawingContext &context,
1333 const wxRect &rect, const wxRect &panelRect, unsigned iPass ) override
1334 {
1335 auto result = rect;
1336 if ( iPass == TrackArtist::PassBorders ) {
1337 if ( true ) {
1338 wxCoord textWidth, textHeight;
1339 GetTrackNameExtent(context.dc, *mpChannel, &textWidth, &textHeight);
1340 result =
1341 GetTrackNameRect( mLeftOffset, rect, textWidth, textHeight );
1342 }
1343 }
1344 return result;
1345 }
1346
1347 std::shared_ptr<Channel> mpChannel;
1350};
1351
1352//Simply fills area using specified brush and outlines borders
1354{
1355 //Required to keep selection behaviour similar to others
1356 std::shared_ptr<Channel> mpChannel;
1358public:
1363 const std::shared_ptr<Channel>& pChannel, int fillBrushName
1364 ) : mpChannel{ pChannel }, mFillBrushName{ fillBrushName }
1365 {
1366 }
1367
1369
1371 const wxRect& rect, unsigned iPass) override
1372 {
1373 if (iPass == TrackArtist::PassBackground)
1374 {
1375 context.dc.SetPen(*wxTRANSPARENT_PEN);
1376 AColor::UseThemeColour(&context.dc, mFillBrushName);
1377 context.dc.DrawRectangle(rect);
1378 wxRect bevel(rect.x, rect.y, rect.width - 1, rect.height - 1);
1379 AColor::BevelTrackInfo(context.dc, true, bevel, false);
1380 }
1381 }
1382
1383 std::shared_ptr<Track> DoFindTrack() override
1384 {
1385 return GetTrack(*mpChannel).shared_from_this();
1386 }
1387
1388 std::vector<UIHandlePtr> HitTest(const TrackPanelMouseState& state,
1389 const AudacityProject* pProject) override
1390 {
1391 return {};
1392 }
1393};
1394
1395//Simply place children one after another horizontally, without any specific logic
1397
1399
1401 : mRefinement(std::move(refinement))
1402 {
1403 }
1404
1405 Subdivision Children(const wxRect& /*rect*/) override
1406 {
1407 return { Axis::X, mRefinement };
1408 }
1409
1410};
1411
1412
1413// optional affordance area, and n channels with vertical rulers,
1414// alternating with n - 1 resizers;
1415// each channel-ruler pair might be divided into multiple views
1420 ChannelStack(const std::shared_ptr<Track> &pTrack, wxCoord leftOffset)
1421 : mpTrack{ pTrack }, mLeftOffset{ leftOffset } {}
1422 Subdivision Children(const wxRect &rect_) override
1423 {
1424 auto rect = rect_;
1425 Refinement refinement;
1426
1427 const auto channels = mpTrack->Channels();
1428 const auto pLast = *channels.rbegin();
1429 wxCoord yy = rect.GetTop();
1430 auto heights = FindAdjustedChannelHeights(*mpTrack);
1431 auto pHeight = heights.begin();
1432 for (auto pChannel : channels) {
1433 auto &view = ChannelView::Get(*pChannel);
1434 if (auto affordance = view.GetAffordanceControls()) {
1435 auto panelRect = std::make_shared<EmptyPanelRect>(pChannel,
1436 mpTrack->GetSelected()
1437 ? clrTrackInfoSelected : clrTrackInfo);
1438 Refinement hgroup {
1439 std::make_pair(rect.GetLeft() + 1, panelRect),
1440 std::make_pair(mLeftOffset, affordance)
1441 };
1442 refinement
1443 .emplace_back(yy, std::make_shared<HorizontalGroup>(hgroup));
1445 }
1446
1447 auto height = *pHeight++;
1448 rect.SetTop(yy);
1449 rect.SetHeight(height - kChannelSeparatorThickness);
1450 refinement.emplace_back(yy,
1451 std::make_shared<VRulersAndChannels>(pChannel,
1452 ChannelView::Get(*pChannel).GetSubViews(rect),
1453 mLeftOffset));
1454 if (pChannel != pLast) {
1455 yy += height;
1456 refinement.emplace_back(
1459 .shared_from_this());
1460 }
1461 }
1462
1463 return { Axis::Y, std::move(refinement) };
1464 }
1465
1467 const wxRect& rect, unsigned iPass) override
1468 {
1469 TrackPanelGroup::Draw(context, rect, iPass);
1470 if (iPass == TrackArtist::PassFocus && mpTrack->IsSelected()) {
1471 const auto channels = mpTrack->Channels();
1472 const auto pLast = *channels.rbegin();
1473 wxCoord yy = rect.GetTop();
1474 assert(mpTrack->IsLeader()); // by construction
1475 auto heights = FindAdjustedChannelHeights(*mpTrack);
1476 auto pHeight = heights.begin();
1477 for (auto pChannel : channels) {
1478 auto& view = ChannelView::Get(*pChannel);
1479 auto height = *pHeight++;
1480 if (auto affordance = view.GetAffordanceControls())
1481 height += kAffordancesAreaHeight;
1482 auto trackRect = wxRect(
1483 mLeftOffset,
1484 yy,
1485 rect.GetRight() - mLeftOffset,
1487 TrackArt::DrawCursor(context, trackRect, mpTrack.get());
1488 yy += height;
1489 }
1490 }
1491 }
1492
1493 const std::shared_ptr<Track> mpTrack;
1495};
1496
1497// A track control panel, left of n vertical rulers and n channels
1498// alternating with n - 1 resizers
1504 const std::shared_ptr<Track> &pTrack, wxCoord leftOffset)
1505 : mpTrack{ pTrack }, mLeftOffset{ leftOffset } {}
1506 Subdivision Children(const wxRect &rect) override
1507 { return { Axis::X, Refinement{
1508 { rect.GetLeft(),
1509 TrackControls::Get(*mpTrack).shared_from_this() },
1510 { rect.GetLeft() + kTrackInfoWidth,
1511 std::make_shared<ChannelStack>(mpTrack, mLeftOffset) }
1512 } }; }
1513
1514 // TrackPanelDrawable implementation
1516 const wxRect &rect, unsigned iPass) override
1517 {
1518 if (iPass == TrackArtist::PassBorders) {
1519 auto &dc = context.dc;
1520 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1521 dc.SetPen(*wxBLACK_PEN);
1522
1523 // border
1524 dc.DrawRectangle(
1525 rect.x, rect.y,
1526 rect.width - kShadowThickness, rect.height - kShadowThickness
1527 );
1528
1529 // shadow
1530 // Stroke lines along bottom and right, which are slightly short at
1531 // bottom-left and top-right
1532 const auto right = rect.GetRight();
1533 const auto bottom = rect.GetBottom();
1534
1535 // bottom
1536 AColor::Line(dc, rect.x + 2, bottom, right, bottom);
1537 // right
1538 AColor::Line(dc, right, rect.y + 2, right, bottom);
1539 }
1540 if (iPass == TrackArtist::PassFocus) {
1541 // Sometimes highlight is not drawn on backing bitmap. I thought
1542 // it was because FindFocus did not return the TrackPanel on Mac, but
1543 // when I removed that test, yielding this condition:
1544 // if (GetFocusedTrack() != NULL) {
1545 // the highlight was reportedly drawn even when something else
1546 // was the focus and no highlight should be drawn. -RBD
1547 const auto artist = TrackArtist::Get(context);
1548 auto &trackPanel = *artist->parent;
1549 auto &trackFocus = TrackFocus::Get( *trackPanel.GetProject() );
1550 if (trackFocus.Get() == mpTrack.get() &&
1551 wxWindow::FindFocus() == &trackPanel) {
1553 wxRect theRect = rect;
1554 auto &dc = context.dc;
1555 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1556
1557 AColor::TrackFocusPen(&dc, 2);
1558 dc.DrawRectangle(theRect);
1559 theRect.Deflate(1);
1560
1561 AColor::TrackFocusPen(&dc, 1);
1562 dc.DrawRectangle(theRect);
1563 theRect.Deflate(1);
1564
1565 AColor::TrackFocusPen(&dc, 0);
1566 dc.DrawRectangle(theRect);
1567 }
1568 }
1569 }
1570
1572 const wxRect &rect, const wxRect &, unsigned iPass) override
1573 {
1574 if (iPass == TrackArtist::PassBorders)
1575 return {
1576 rect.x - kBorderThickness,
1577 rect.y - kBorderThickness,
1578 rect.width + 2 * kBorderThickness + kShadowThickness,
1579 rect.height + 2 * kBorderThickness + kShadowThickness
1580 };
1581 else if (iPass == TrackArtist::PassFocus) {
1582 constexpr auto extra = kBorderThickness + 3;
1583 return {
1584 rect.x - extra,
1585 rect.y - extra,
1586 rect.width + 2 * extra + kShadowThickness,
1587 rect.height + 2 * extra + kShadowThickness
1588 };
1589 }
1590 else
1591 return rect;
1592 }
1593
1594 const std::shared_ptr<Track> mpTrack;
1596};
1597
1598// Stacks a label and a single or multi-channel track on a resizer below,
1599// which is associated with the last channel
1605 const std::shared_ptr<Track> &pTrack, wxCoord leftOffset)
1606 : mpTrack{ pTrack }, mLeftOffset{ leftOffset } {}
1607 Subdivision Children(const wxRect &rect) override
1608 { return { Axis::Y, Refinement{
1609 { rect.GetTop(),
1610 std::make_shared<LabeledChannelGroup>(mpTrack, mLeftOffset) },
1611 { rect.GetTop() + rect.GetHeight() - kTrackSeparatorThickness,
1613 **mpTrack->Channels().rbegin()).shared_from_this()
1614 }
1615 } }; }
1616 const std::shared_ptr<Track> mpTrack;
1618};
1619
1620// Stacks a dead area at top, the tracks, and the click-to-deselect area below
1622 explicit Subgroup(TrackPanel &panel) : mPanel{ panel } {}
1623 Subdivision Children(const wxRect &rect) override
1624 {
1625 const auto &viewInfo = *mPanel.GetViewInfo();
1626 wxCoord yy = -viewInfo.vpos;
1627 Refinement refinement;
1628
1629 auto &tracks = *mPanel.GetTracks();
1630 if (!tracks.empty())
1631 refinement.emplace_back( yy, EmptyCell::Instance() ),
1632 yy += kTopMargin;
1633
1634 for (const auto leader : tracks) {
1635 wxCoord height = 0;
1636 for (auto pChannel : leader->Channels()) {
1637 auto &view = ChannelView::Get(*pChannel);
1638 height += view.GetHeight();
1639 }
1640 refinement.emplace_back( yy,
1641 std::make_shared<ResizingChannelGroup>(
1642 leader->SharedPointer(), viewInfo.GetLeftOffset())
1643 );
1644 yy += height;
1645 }
1646
1647 refinement.emplace_back(std::max(0, yy), mPanel.GetBackgroundCell());
1648
1649 return { Axis::Y, std::move(refinement) };
1650 }
1652};
1653
1654// Main group shaves off the left and right margins
1656 explicit MainGroup(TrackPanel &panel) : mPanel{ panel } {}
1657 Subdivision Children(const wxRect &rect) override
1658 { return { Axis::X, Refinement{
1659 { 0, EmptyCell::Instance() },
1660 { kLeftMargin, std::make_shared<Subgroup>(mPanel) },
1661 { rect.GetRight() + 1 - kRightMargin, EmptyCell::Instance() }
1662 } }; }
1664};
1665
1666}
1667
1668std::shared_ptr<TrackPanelNode> TrackPanel::Root()
1669{
1670 // Root and other subgroup objects are throwaways.
1671 // They might instead be cached to avoid repeated allocation.
1672 // That cache would need invalidation when there is addition, deletion, or
1673 // permutation of tracks, or change of width of the vertical rulers.
1674 return std::make_shared< MainGroup >( *this );
1675}
1676
1677// This finds the rectangle of a given track (including all channels),
1678// either that of the label 'adornment' or the track itself
1679// The given track is assumed to be the first channel
1680wxRect TrackPanel::FindTrackRect( const Track * target )
1681{
1682 auto leader = *GetTracks()->Find( target );
1683 if (!leader) {
1684 return {};
1685 }
1686
1687 return CellularPanel::FindRect( [&] ( TrackPanelNode &node ) {
1688 if (auto pGroup = dynamic_cast<const LabeledChannelGroup*>( &node ))
1689 return pGroup->mpTrack.get() == leader;
1690 return false;
1691 } );
1692}
1693
1695{
1696 auto rect = FindTrackRect(target);
1697 if (rect != wxRect{}) {
1698 // Enlarge horizontally.
1699 // PRL: perhaps it's one pixel too much each side, including some gray
1700 // beyond the yellow?
1701 rect.x = 0;
1702 GetClientSize(&rect.width, nullptr);
1703
1704 // Enlarge vertically, enough to enclose the yellow focus border pixels
1705 // The the outermost ring of gray pixels is included on three sides
1706 // but not the top (should that be fixed?)
1707
1708 // (Note that TrackPanel paints its focus over the "top margin" of the
1709 // rectangle allotted to the track, according to ChannelView::GetY() and
1710 // ChannelView::GetHeight(), but also over the margin of the next track.)
1711
1712 rect.height += kBottomMargin;
1713 int dy = kTopMargin - 1;
1714 rect.Inflate( 0, dy );
1715
1716 // Note that this rectangle does not coincide with any one of
1717 // the nodes in the subdivision.
1718 }
1719 return rect;
1720}
1721
1722std::vector<wxRect> TrackPanel::FindRulerRects(const Channel &target)
1723{
1724 std::vector<wxRect> results;
1725 VisitCells( [&](const wxRect &rect, TrackPanelCell &visited) {
1726 if (auto pRuler = dynamic_cast<const ChannelVRulerControls*>(&visited))
1727 if (auto pView = pRuler->GetChannelView())
1728 if (pView->FindChannel().get() == &target)
1729 results.push_back(rect);
1730 } );
1731 return results;
1732}
1733
1735{
1736 // Note that focus track is always a leader
1737 auto pTrack = TrackFocus::Get(*GetProject()).Get();
1738 return pTrack
1739 ? &ChannelView::Get(*pTrack->GetChannel(0))
1740 : GetBackgroundCell().get();
1741}
1742
1744{
1745 // This may have a side-effect of assigning a focus if there was none
1746 auto& trackFocus = TrackFocus::Get(*GetProject());
1747 trackFocus.Set(trackFocus.Get());
1749}
1750
1752{
1753 if (auto cell = GetFocusedCell())
1754 Refresh( false );
1755}
wxImage(22, 22)
END_EVENT_TABLE()
const TranslatableString name
Definition: Distortion.cpp:76
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
THEME_API Theme theTheme
Definition: Theme.cpp:82
constexpr auto kTimerInterval
#define A(N)
Definition: ToChars.cpp:62
static Settings & settings()
Definition: TrackInfo.cpp:83
std::unique_ptr< wxCursor > MakeCursor(int WXUNUSED(CursorId), const char *const pXpm[36], int HotX, int HotY)
Definition: TrackPanel.cpp:187
bool within(A a, B b, DIST d)
Definition: TrackPanel.cpp:168
@ 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:463
static void TrackFocusPen(wxDC *dc, int level)
Definition: AColor.cpp:481
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:454
static void UseThemeColour(wxDC *dc, int iBrush, int iPen=-1, int alpha=255)
Definition: AColor.cpp:372
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:123
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 DoContextMenu(TrackPanelCell *pCell=nullptr)
void VisitCells(const SimpleCellVisitor &visitor)
UIHandlePtr Target()
ViewInfo * mViewInfo
FoundCell FindCell(int mouseX, int mouseY)
wxRect FindRect(const TrackPanelCell &cell)
wxMouseState mLastMouseState
wxCoord MostRecentXCoord() const
void HandleCursorForPresentMouseState(bool doHit=true)
IteratorRange< ChannelIterator< ChannelType > > Channels()
Get range of channels with mutative access.
Definition: Channel.h:408
std::shared_ptr< ChannelType > GetChannel(size_t iChannel)
Retrieve a channel, cast to the given type.
Definition: Channel.h:344
ChannelGroup & GetChannelGroup()
Channel object's lifetime is assumed to be nested in its Track's.
Definition: Channel.cpp:71
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:120
virtual Refinement GetSubViews(const wxRect &rect)
int GetHeight() const
std::pair< int, int > vrulerSize
Definition: ChannelView.h:128
static int GetChannelGroupHeight(const Track *pTrack)
Definition: ChannelView.cpp:32
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:333
Subclass & Get(const RegisteredFactory &key)
Get reference to an attachment, creating on demand if not present, down-cast it to Subclass.
Definition: ClientData.h:309
void Assign(const RegisteredFactory &key, ReplacementPointer &&replacement)
Reassign Site's pointer to ClientData.
Definition: ClientData.h:355
@ 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)
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)
void ModifyState(bool bWantsAutoSave)
static ProjectHistory & 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:41
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:26
wxColour & Colour(int iIndex)
static TrackArtist * Get(TrackPanelDrawingContext &)
Definition: TrackArtist.cpp:69
static TrackControls & Get(Track &track)
static TrackFocus & Get(AudacityProject &project)
Track * Get()
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:123
void EnsureVisible(bool modifyState=false)
Definition: Track.cpp:86
std::shared_ptr< Track > SubstitutePendingChangedTrack()
Definition: Track.cpp:1160
bool IsLeader() const override
Definition: Track.cpp:298
std::shared_ptr< Subclass > Lock(const std::weak_ptr< Subclass > &wTrack)
Definition: Track.h:1237
TrackIter< Track > Find(Track *pTrack)
Definition: Track.cpp:536
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1091
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:354
void UpdatePendingTracks()
Definition: Track.cpp:1026
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:1108
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1158
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:66
void UpdateVRuler(Track *t)
Definition: TrackPanel.cpp:933
void UpdatePrefs() override
Definition: TrackPanel.cpp:355
Observer::Subscription mProjectRulerInvalidatedSubscription
Definition: TrackPanel.h:198
void HandlePageUpKey()
Definition: TrackPanel.cpp:599
AdornedRulerPanel * mRuler
Definition: TrackPanel.h:204
size_t GetSelectedTrackCount() const
Definition: TrackPanel.cpp:635
TrackPanelListener * mListener
Definition: TrackPanel.h:200
void UpdateViewIfNoTracks()
Definition: TrackPanel.cpp:640
std::shared_ptr< TrackList > mTracks
Definition: TrackPanel.h:202
double GetMostRecentXPos()
Definition: TrackPanel.cpp:741
virtual ~TrackPanel()
Definition: TrackPanel.cpp:345
std::shared_ptr< TrackPanelNode > Root() override
void ProcessUIHandleResult(TrackPanelCell *pClickedTrack, TrackPanelCell *pLatestCell, unsigned refreshResult) override
Definition: TrackPanel.cpp:534
Observer::Subscription mAudioIOSubscription
Definition: TrackPanel.h:193
std::shared_ptr< CommonTrackPanelCell > GetBackgroundCell()
Definition: TrackPanel.cpp:870
AudacityProject * GetProject() const override
Definition: TrackPanel.cpp:366
void OnEnsureVisible(const TrackListEvent &event)
void OnSize(wxSizeEvent &)
Definition: TrackPanel.cpp:380
Observer::Subscription mTrackListSubscription
Definition: TrackPanel.h:192
SelectedRegion mLastDrawnSelectedRegion
Definition: TrackPanel.h:231
void Refresh(bool eraseBackground=true, const wxRect *rect=(const wxRect *) NULL) override
Definition: TrackPanel.cpp:784
void OnTrackMenu(Track *t=NULL)
const TrackList * GetTracks() const
Definition: TrackPanel.h:172
static TrackPanel & Get(AudacityProject &project)
Definition: TrackPanel.cpp:232
void OnTrackFocusChange(struct TrackFocusChangeMessage)
void UpdateSelectionDisplay()
Definition: TrackPanel.cpp:624
void HandlePageDownKey()
Definition: TrackPanel.cpp:604
Observer::Subscription mRealtimeEffectManagerSubscription
Definition: TrackPanel.h:196
std::shared_ptr< CommonTrackPanelCell > mpBackground
Definition: TrackPanel.h:235
wxRect FindFocusedTrackRect(const Track *target)
Observer::Subscription mSyncLockSubscription
Definition: TrackPanel.h:197
void OnTimer(wxTimerEvent &event)
AS: This gets called on our wx timer events.
Definition: TrackPanel.cpp:409
void OnKeyDown(wxKeyEvent &event)
Definition: TrackPanel.cpp:692
void SetFocusedCell() override
void UpdateStatusMessage(const TranslatableString &status) override
Definition: TrackPanel.cpp:615
static void Destroy(AudacityProject &project)
Definition: TrackPanel.cpp:242
void UpdateVRulerSize()
Definition: TrackPanel.cpp:981
void OnTrackListDeletion()
Definition: TrackPanel.cpp:677
bool mRefreshBacking
Definition: TrackPanel.h:226
wxRect FindTrackRect(const Track *target)
std::vector< wxRect > FindRulerRects(const Channel &target)
void OnMouseEvent(wxMouseEvent &event)
Definition: TrackPanel.cpp:712
void DrawTracks(wxDC *dc)
Definition: TrackPanel.cpp:820
void SetBackgroundCell(const std::shared_ptr< CommonTrackPanelCell > &pCell)
Definition: TrackPanel.cpp:865
TrackPanel::AudacityTimer mTimer
void OnUndoReset(struct UndoRedoMessage)
Definition: TrackPanel.cpp:466
Observer::Subscription mUndoSubscription
Definition: TrackPanel.h:194
void UpdateVRulers()
Definition: TrackPanel.cpp:925
int mTimeCount
Definition: TrackPanel.h:224
void VerticalScroll(float fracPosition)
void OnAudioIO(AudioIOEvent)
Definition: TrackPanel.cpp:807
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:255
void OnSyncLockChange(struct SyncLockChangeMessage)
Definition: TrackPanel.cpp:461
bool IsAudioActive()
Definition: TrackPanel.cpp:609
void MakeParentRedrawScrollbars()
Definition: TrackPanel.cpp:519
void OnIdle(wxIdleEvent &event)
Definition: TrackPanel.cpp:388
void OnPaint(wxPaintEvent &event)
Definition: TrackPanel.cpp:476
Observer::Subscription mFocusChangeSubscription
Definition: TrackPanel.h:195
void RefreshTrack(Track *trk, bool refreshbacking=true)
Definition: TrackPanel.cpp:747
void UpdateTrackVRuler(Track &t)
Definition: TrackPanel.cpp:941
std::unique_ptr< TrackArtist > mTrackArtist
Definition: TrackPanel.h:206
TrackPanelCell * GetFocusedCell() override
void OnTrackListResizing(const TrackListEvent &event)
Definition: TrackPanel.cpp:664
virtual void TP_HandleResize()=0
virtual void TP_ScrollWindow(double scrollto)=0
virtual bool TP_ScrollUpDown(int delta)=0
virtual void TP_RedrawScrollbars()=0
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:38
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:71
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:219
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
int scrollStep
Definition: ViewInfo.h:237
void SetHeight(int height)
Definition: ViewInfo.h:202
double PositionToTime(int64 position, int64 origin=0, bool ignoreFisheye=false) const
Definition: ZoomInfo.cpp:35
static double GetDefaultZoom()
Definition: ZoomInfo.h:113
void SetWidth(int width)
Definition: ZoomInfo.h:86
double GetScreenEndTime() const
Definition: ZoomInfo.h:104
int GetVRulerOffset() const
Definition: ZoomInfo.h:90
double h
Definition: ZoomInfo.h:52
void SetVRulerWidth(int width)
Definition: ZoomInfo.h:89
int GetLeftOffset() const
Definition: ZoomInfo.h:93
void SetZoom(double pixelsPerSecond)
Definition: ZoomInfo.cpp:89
int GetVRulerWidth() const
Definition: ZoomInfo.h:88
int vpos
Definition: ZoomInfo.h:50
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 CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:208
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:196
std::unique_ptr< WindowPlacement > FindFocus()
Find the window that is accepting keyboard input, if any.
Definition: BasicUI.h:343
void Capture(wxWindow *handler)
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
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:802
const Track & GetTrack(const Channel &channel)
AttachedWindows::RegisteredFactory sKey
Definition: TrackPanel.cpp:208
std::shared_ptr< Track > FindTrack(TrackPanelCell *pCell)
Definition: TrackPanel.cpp:525
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:879
void DrawTrackName(int leftOffset, TrackPanelDrawingContext &context, const Channel &channel, const wxRect &rect)
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
Notification of changes in individual tracks of TrackList, or of TrackList's composition.
Definition: Track.h:937
const int mExtra
Definition: Track.h:976
const std::weak_ptr< Track > mpTrack
Definition: Track.h:975
const Type mType
Definition: Track.h:974
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