diff --git a/mac/Audacity.xcodeproj/project.pbxproj b/mac/Audacity.xcodeproj/project.pbxproj index 8587091da..d638d4621 100644 --- a/mac/Audacity.xcodeproj/project.pbxproj +++ b/mac/Audacity.xcodeproj/project.pbxproj @@ -1208,6 +1208,10 @@ 28FC1AFB0A47762C00A188AE /* WrappedType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28FC1AF90A47762C00A188AE /* WrappedType.cpp */; }; 28FE4A080ABF4E960056F5C4 /* mmx_optimized.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28FE4A060ABF4E960056F5C4 /* mmx_optimized.cpp */; }; 28FE4A090ABF4E960056F5C4 /* sse_optimized.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28FE4A070ABF4E960056F5C4 /* sse_optimized.cpp */; }; + 5E74D2D81CC4425D00D88B0B /* TrackPanelOverlay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E74D2D61CC4425D00D88B0B /* TrackPanelOverlay.cpp */; }; + 5E74D2E31CC4429700D88B0B /* EditCursorOverlay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E74D2DD1CC4429700D88B0B /* EditCursorOverlay.cpp */; }; + 5E74D2E41CC4429700D88B0B /* PlayIndicatorOverlay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E74D2DF1CC4429700D88B0B /* PlayIndicatorOverlay.cpp */; }; + 5E74D2E51CC4429700D88B0B /* Scrubbing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E74D2E11CC4429700D88B0B /* Scrubbing.cpp */; }; 8406A93812D0F2510011EA01 /* EQDefaultCurves.xml in Resources */ = {isa = PBXBuildFile; fileRef = 8406A93712D0F2510011EA01 /* EQDefaultCurves.xml */; }; 8484F31413086237002DF7F0 /* DeviceManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8484F31213086237002DF7F0 /* DeviceManager.cpp */; }; ED15214D163C22F000451B5F /* lsr.c in Sources */ = {isa = PBXBuildFile; fileRef = ED152123163C220300451B5F /* lsr.c */; }; @@ -2968,6 +2972,16 @@ 28FE4A070ABF4E960056F5C4 /* sse_optimized.cpp */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 3; lastKnownFileType = sourcecode.cpp.cpp; path = sse_optimized.cpp; sourceTree = ""; tabWidth = 3; }; 28FEC1B21A12B6FB00FACE48 /* EffectAutomationParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EffectAutomationParameters.h; path = ../include/audacity/EffectAutomationParameters.h; sourceTree = SOURCE_ROOT; }; 5E61EE0C1CBAA6BB0009FCF1 /* MemoryX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryX.h; sourceTree = ""; }; + 5E74D2D61CC4425D00D88B0B /* TrackPanelOverlay.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TrackPanelOverlay.cpp; sourceTree = ""; }; + 5E74D2D71CC4425D00D88B0B /* TrackPanelOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackPanelOverlay.h; sourceTree = ""; }; + 5E74D2D91CC4427B00D88B0B /* TrackPanelCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackPanelCell.h; sourceTree = ""; }; + 5E74D2DA1CC4427B00D88B0B /* TrackPanelCellIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackPanelCellIterator.h; sourceTree = ""; }; + 5E74D2DD1CC4429700D88B0B /* EditCursorOverlay.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EditCursorOverlay.cpp; sourceTree = ""; }; + 5E74D2DE1CC4429700D88B0B /* EditCursorOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EditCursorOverlay.h; sourceTree = ""; }; + 5E74D2DF1CC4429700D88B0B /* PlayIndicatorOverlay.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlayIndicatorOverlay.cpp; sourceTree = ""; }; + 5E74D2E01CC4429700D88B0B /* PlayIndicatorOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlayIndicatorOverlay.h; sourceTree = ""; }; + 5E74D2E11CC4429700D88B0B /* Scrubbing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Scrubbing.cpp; sourceTree = ""; }; + 5E74D2E21CC4429700D88B0B /* Scrubbing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scrubbing.h; sourceTree = ""; }; 5ED18DB61CC16B1E00FAFE95 /* Reverb_libSoX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reverb_libSoX.h; sourceTree = ""; }; 5ED18DB71CC290AB00FAFE95 /* wxFileNameWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wxFileNameWrapper.h; sourceTree = ""; }; 82FF184D13CF01A600C1B664 /* dBTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dBTable.cpp; path = sbsms/src/dBTable.cpp; sourceTree = ""; }; @@ -3793,7 +3807,6 @@ 1790AFC409883BFD008A330A /* src */ = { isa = PBXGroup; children = ( - 5ED18DB71CC290AB00FAFE95 /* wxFileNameWrapper.h */, 1790AFC709883BFD008A330A /* AboutDialog.cpp */, 1790AFC909883BFD008A330A /* AColor.cpp */, 1790AFCE09883BFD008A330A /* AudacityApp.cpp */, @@ -3870,6 +3883,7 @@ 1790B0EA09883BFD008A330A /* TrackArtist.cpp */, 1790B0EC09883BFD008A330A /* TrackPanel.cpp */, 1790B0EE09883BFD008A330A /* TrackPanelAx.cpp */, + 5E74D2D61CC4425D00D88B0B /* TrackPanelOverlay.cpp */, 1790B0F209883BFD008A330A /* UndoManager.cpp */, 28C8211C1B5C661E00B53328 /* ViewInfo.cpp */, 1790B0F709883BFD008A330A /* VoiceKey.cpp */, @@ -3964,7 +3978,10 @@ 1790B0EB09883BFD008A330A /* TrackArtist.h */, 1790B0ED09883BFD008A330A /* TrackPanel.h */, 1790B0EF09883BFD008A330A /* TrackPanelAx.h */, + 5E74D2D91CC4427B00D88B0B /* TrackPanelCell.h */, + 5E74D2DA1CC4427B00D88B0B /* TrackPanelCellIterator.h */, 2803C8B619F35AA000278526 /* TrackPanelListener.h */, + 5E74D2D71CC4425D00D88B0B /* TrackPanelOverlay.h */, 284416391B82D6BC0000574D /* TranslatableStringArray.h */, 1790B0F309883BFD008A330A /* UndoManager.h */, 1790B0F609883BFD008A330A /* ViewInfo.h */, @@ -3973,6 +3990,7 @@ 1790B0FC09883BFD008A330A /* WaveTrack.h */, 2844163A1B82D6BC0000574D /* WaveTrackLocation.h */, 28FC1AFA0A47762C00A188AE /* WrappedType.h */, + 5ED18DB71CC290AB00FAFE95 /* wxFileNameWrapper.h */, 1790AFDC09883BFD008A330A /* blockfile */, 174D9025098C78AF00D5909F /* commands */, 1790AFFD09883BFD008A330A /* effects */, @@ -3981,6 +3999,7 @@ 1841B4FD0E00AD3D00F386E9 /* ondemand */, 1790B0B509883BFD008A330A /* prefs */, 2897F6DB0AB3DB5A003C20C5 /* toolbars */, + 5E74D2DB1CC4429700D88B0B /* tracks */, 1790B0FD09883BFD008A330A /* widgets */, 1790B10D09883BFD008A330A /* xml */, ); @@ -5516,6 +5535,27 @@ path = expat/lib; sourceTree = ""; }; + 5E74D2DB1CC4429700D88B0B /* tracks */ = { + isa = PBXGroup; + children = ( + 5E74D2DC1CC4429700D88B0B /* ui */, + ); + path = tracks; + sourceTree = ""; + }; + 5E74D2DC1CC4429700D88B0B /* ui */ = { + isa = PBXGroup; + children = ( + 5E74D2DD1CC4429700D88B0B /* EditCursorOverlay.cpp */, + 5E74D2DE1CC4429700D88B0B /* EditCursorOverlay.h */, + 5E74D2DF1CC4429700D88B0B /* PlayIndicatorOverlay.cpp */, + 5E74D2E01CC4429700D88B0B /* PlayIndicatorOverlay.h */, + 5E74D2E11CC4429700D88B0B /* Scrubbing.cpp */, + 5E74D2E21CC4429700D88B0B /* Scrubbing.h */, + ); + path = ui; + sourceTree = ""; + }; ED05D0FF0E50AD5600CC4BD3 /* libscorealign */ = { isa = PBXGroup; children = ( @@ -7289,6 +7329,7 @@ 1790B14F09883BFD008A330A /* SoundTouchEffect.cpp in Sources */, 1790B15109883BFD008A330A /* StereoToMono.cpp in Sources */, 1790B15209883BFD008A330A /* ToneGen.cpp in Sources */, + 5E74D2E31CC4429700D88B0B /* EditCursorOverlay.cpp in Sources */, 1790B15309883BFD008A330A /* TruncSilence.cpp in Sources */, 1790B15409883BFD008A330A /* TwoPassSimpleMono.cpp in Sources */, 1790B15809883BFD008A330A /* Wahwah.cpp in Sources */, @@ -7421,6 +7462,7 @@ 1841B50B0E00AD6E00F386E9 /* ODManager.cpp in Sources */, 1841B50C0E00AD6E00F386E9 /* ODTask.cpp in Sources */, 1841B50D0E00AD6E00F386E9 /* ODTaskThread.cpp in Sources */, + 5E74D2E51CC4429700D88B0B /* Scrubbing.cpp in Sources */, 1841B50E0E00AD6E00F386E9 /* ODWaveTrackTaskQueue.cpp in Sources */, 1841B5110E00AD8D00F386E9 /* ODPCMAliasBlockFile.cpp in Sources */, 2860BA240E0F0D8600A13878 /* SoundActivatedRecord.cpp in Sources */, @@ -7540,6 +7582,7 @@ ED920CAF15B19F61008CA12C /* ModulePrefs.cpp in Sources */, EDD2431416934A6100D9DEC2 /* BassTreble.cpp in Sources */, ED19449A1733F92800F4F5CA /* Reverb.cpp in Sources */, + 5E74D2D81CC4425D00D88B0B /* TrackPanelOverlay.cpp in Sources */, 2849A42017F8BEC2005C653F /* KeyView.cpp in Sources */, 284FD04217FC72A50009A025 /* ScienFilter.cpp in Sources */, 284FD04517FC72EE0009A025 /* Biquad.cpp in Sources */, @@ -7559,6 +7602,7 @@ 28001B3E1A0F0E5D007DD161 /* NumericTextCtrl.cpp in Sources */, 28001B4B1A0F0EB6007DD161 /* SpectralSelectionBar.cpp in Sources */, 28BB98051A15BE6800D1CC80 /* NoiseReduction.cpp in Sources */, + 5E74D2E41CC4429700D88B0B /* PlayIndicatorOverlay.cpp in Sources */, 28D000A51A32920C00367B21 /* DeviceChange.cpp in Sources */, 28D8425C1AD8D69D00551353 /* SelectedRegion.cpp in Sources */, 2888A1631AE25F9A00E06FDC /* Diags.cpp in Sources */, diff --git a/src/Makefile.am b/src/Makefile.am index fc1869b2b..6fa576381 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -233,7 +233,11 @@ audacity_SOURCES = \ TrackPanel.h \ TrackPanelAx.cpp \ TrackPanelAx.h \ + TrackPanelCell.h \ + TrackPanelCellIterator.h \ TrackPanelListener.h \ + TrackPanelOverlay.cpp \ + TrackPanelOverlay.h \ TranslatableStringArray.h \ UndoManager.cpp \ UndoManager.h \ @@ -523,6 +527,12 @@ audacity_SOURCES = \ toolbars/ToolsToolBar.h \ toolbars/TranscriptionToolBar.cpp \ toolbars/TranscriptionToolBar.h \ + tracks/ui/EditCursorOverlay.cpp \ + tracks/ui/EditCursorOverlay.h \ + tracks/ui/PlayIndicatorOverlay.cpp \ + tracks/ui/PlayIndicatorOverlay.h \ + tracks/ui/Scrubbing.cpp \ + tracks/ui/Scrubbing.h \ widgets/AButton.cpp \ widgets/AButton.h \ widgets/ASlider.cpp \ diff --git a/src/Project.cpp b/src/Project.cpp index 4891e4ce4..fab8eec9e 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -156,6 +156,10 @@ scroll information. It also has some status flags. #include "toolbars/ToolsToolBar.h" #include "toolbars/TranscriptionToolBar.h" +#include "tracks/ui/EditCursorOverlay.h" +#include "tracks/ui/PlayIndicatorOverlay.h" +#include "tracks/ui/Scrubbing.h" + #include "commands/ScriptCommandRelay.h" #include "commands/CommandDirectory.h" #include "commands/CommandTargets.h" @@ -924,6 +928,32 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, this, mRuler); + mIndicatorOverlay = std::make_unique(this); + + mCursorOverlay = std::make_unique(this); + +#ifdef EXPERIMENTAL_SCRUBBING_BASIC + // This must follow construction of *mIndicatorOverlay, because it must + // attach its timer event handler later (so that its handler is invoked + // earlier) + mScrubOverlay = std::make_unique(this); +#else + mScrubOverlay = NULL; +#endif + + // This must follow construction of *mScrubOverlay, because it must + // attach its timer event handler later (so that its handler is invoked + // earlier) + this->Connect(EVT_TRACK_PANEL_TIMER, + wxCommandEventHandler(ViewInfo::OnTimer), + NULL, + &mViewInfo); + + // Add the overlays, in the sequence in which they will be painted + mTrackPanel->AddOverlay(mIndicatorOverlay.get()); + mTrackPanel->AddOverlay(mCursorOverlay.get()); + mTrackPanel->AddOverlay(mScrubOverlay.get()); + // LLL: When Audacity starts or becomes active after returning from // another application, the first window that can accept focus // will be given the focus even if we try to SetFocus(). By @@ -1051,6 +1081,12 @@ AudacityProject::~AudacityProject() wxGetApp().GetRecentFiles()->RemoveMenu(mRecentFilesMenu); } + if(mTrackPanel) { + mTrackPanel->RemoveOverlay(mScrubOverlay.get()); + mTrackPanel->RemoveOverlay(mCursorOverlay.get()); + mTrackPanel->RemoveOverlay(mIndicatorOverlay.get()); + } + wxTheApp->Disconnect(EVT_AUDIOIO_CAPTURE, wxCommandEventHandler(AudacityProject::OnCapture), NULL, @@ -2339,6 +2375,11 @@ void AudacityProject::OnCloseWindow(wxCloseEvent & event) #endif } + this->Disconnect(EVT_TRACK_PANEL_TIMER, + wxCommandEventHandler(ViewInfo::OnTimer), + NULL, + &mViewInfo); + Destroy(); mIsBeingDeleted = true; diff --git a/src/Project.h b/src/Project.h index b3b358863..d76ccb15d 100644 --- a/src/Project.h +++ b/src/Project.h @@ -62,6 +62,7 @@ class Tags; class EffectPlugs; class TrackPanel; +class TrackPanelOverlay; class FreqWindow; class ContrastDialog; class Meter; @@ -171,9 +172,13 @@ class AUDACITY_DLL_API AudacityProject final : public wxFrame, double GetRate() const { return mRate; } bool ZoomInAvailable() const { return mViewInfo.ZoomInAvailable(); } bool ZoomOutAvailable() const { return mViewInfo.ZoomOutAvailable(); } - double GetSel0() { return mViewInfo.selectedRegion.t0(); } - double GetSel1() { return mViewInfo.selectedRegion.t1(); } + const SelectedRegion &GetSelection() const { return mViewInfo.selectedRegion; } + SelectedRegion &GetSelection() { return mViewInfo.selectedRegion; } + double GetSel0() const { return mViewInfo.selectedRegion.t0(); } + double GetSel1() const { return mViewInfo.selectedRegion.t1(); } const ZoomInfo &GetZoomInfo() const { return mViewInfo; } + const ViewInfo &GetViewInfo() const { return mViewInfo; } + ViewInfo &GetViewInfo() { return mViewInfo; } Track *GetFirstVisible(); void UpdateFirstVisible(); @@ -702,6 +707,10 @@ public: // CommandManager needs to use private methods friend class CommandManager; + // TrackPanelOverlay objects + std::unique_ptr + mIndicatorOverlay, mCursorOverlay, mScrubOverlay; + DECLARE_EVENT_TABLE() }; diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index e917e81fb..d56a6f459 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -155,6 +155,9 @@ is time to refresh some aspect of the screen. #include "Audacity.h" #include "Experimental.h" #include "TrackPanel.h" +#include "TrackPanelCell.h" +#include "TrackPanelCellIterator.h" +#include "TrackPanelOverlay.h" //#define DEBUG_DRAW_TIMING 1 // #define SPECTRAL_EDITING_ESC_KEY @@ -287,12 +290,6 @@ template < class A, class B, class DIST > bool within(A a, B b, DIST d) return (a > b - d) && (a < b + d); } -template < class LOW, class MID, class HIGH > - bool between_incexc(LOW l, MID m, HIGH h) -{ - return (m >= l && m < h); -} - template < class CLIPPEE, class CLIPVAL > void clip_top(CLIPPEE & clippee, CLIPVAL val) { @@ -564,9 +561,6 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id, mSnapLeft = -1; mSnapRight = -1; - mLastCursorX = mNewCursorX = -1; - mLastIndicatorX = mNewIndicatorX = -1; - mCursorTime = -1.0; mOldQPIndicatorPos = -1; // Register for tracklist updates @@ -1023,11 +1017,9 @@ void TrackPanel::OnTimer(wxTimerEvent& ) const double playPos = gAudioIO->GetStreamTime(); - // The sequence of the next two is important. #ifdef EXPERIMENTAL_SCRUBBING_BASIC TimerUpdateScrubbing(playPos); #endif - TimerUpdateIndicator(playPos); DrawOverlays(false); @@ -1140,249 +1132,6 @@ double TrackPanel::GetScreenEndTime() const return mViewInfo->PositionToTime(width, true); } -void TrackPanel::TimerUpdateIndicator(double playPos) -{ - if (!IsAudioActive()) - mNewIndicatorX = -1; - else { - // Calculate the horizontal position of the indicator - - AudacityProject *p = GetProject(); - const bool - onScreen = playPos >= 0.0 && - between_incexc(mViewInfo->h, - playPos, - GetScreenEndTime()); - - // This displays the audio time, too... - DisplaySelection(); - - // BG: Scroll screen if option is set - // msmeyer: But only if not playing looped or in one-second mode - if( mViewInfo->bUpdateTrackIndicator && - p->mLastPlayMode != loopedPlay && - p->mLastPlayMode != oneSecondPlay && - playPos >= 0 && - !onScreen && - !gAudioIO->IsPaused() ) - { - mListener->TP_ScrollWindow( playPos ); - } - - // Always update scrollbars even if not scrolling the window. This is - // important when NEW audio is recorded, because this can change the - // length of the project and therefore the appearance of the scrollbar. - MakeParentRedrawScrollbars(); - - mNewIndicatorX = mViewInfo->TimeToPosition(playPos, GetLeftOffset()); - } -} - -std::pair TrackPanel::GetIndicatorRectangle() -{ - wxRect rect(mLastIndicatorX, 0, 1, mBacking->GetHeight()); -#if defined(__WXMAC__) - rect.Inflate(1, 0); -#endif - - return std::make_pair( - rect, - mLastIndicatorX != mNewIndicatorX - ); -} - -void TrackPanel::UndrawIndicator(wxDC & dc) -{ - // AS: The "indicator" is the little graphical mark shown in the ruler - // that indicates where the current play/record position is. (This also - // draws the moving vertical line.) - - // Erase the old indicator. - if (mLastIndicatorX != -1) - { - int width; - GetTracksUsableArea(&width, NULL); - const bool - onScreen = between_incexc(GetLeftOffset(), - mLastIndicatorX, - GetLeftOffset() + width); - if (onScreen) - { - // LL: Keep from trying to blit outsize of the source DC. This results in a crash on - // OSX due to allocating memory using negative sizes and can be caused by resizing - // the project window while recording or playing. - int w = dc.GetSize().GetWidth(); - if (mLastIndicatorX >= w) { - mLastIndicatorX = w - 1; - } - - // Restore the old position from the backing DC. -#if defined(__WXMAC__) - // On OSX, if a HiDPI resolution is being used, the line will actually take up - // more than 1 pixel (even though it is drawn as 1), so we restore the surrounding - // pixels as well. (This is because the wxClientDC doesn't know about the scaling.) - dc.Blit(mLastIndicatorX - 1, 0, 3, mBacking->GetHeight(), &mBackingDC, mLastIndicatorX - 1, 0); -#else - dc.Blit(mLastIndicatorX, 0, 1, mBacking->GetHeight(), &mBackingDC, mLastIndicatorX, 0); -#endif - } - - mRuler->ClearIndicator(); - } -} - -void TrackPanel::DoDrawIndicator(wxDC & dc) -{ - mLastIndicatorX = mNewIndicatorX; - if (mLastIndicatorX == -1) - return; - - double pos = mViewInfo->PositionToTime(mLastIndicatorX, GetLeftOffset()); - - // Set play/record color - bool rec = (gAudioIO->GetNumCaptureChannels() > 0); - AColor::IndicatorColor( &dc, !rec); - - mRuler->DrawIndicator( pos, rec ); - - // Ensure that we don't draw through the TrackInfo or vertical ruler. - wxRect clip = GetRect(); - int leftCutoff = clip.x + GetLeftOffset(); - int rightCutoff = clip.x + clip.width - kRightMargin; - if (!between_incexc(leftCutoff, mLastIndicatorX, rightCutoff)) - { - return; - } - - // Draw indicator in all visible tracks - VisibleTrackIterator iter( GetProject() ); - for( Track *t = iter.First(); t; t = iter.Next() ) - { - // Don't draw the indicator in label tracks - if( t->GetKind() == Track::Label ) - { - continue; - } - - // Convert virtual coordinate to physical - int y = t->GetY() - mViewInfo->vpos; - - // Draw the NEW indicator in its new location - AColor::Line(dc, - mLastIndicatorX, - y + kTopMargin, - mLastIndicatorX, - // Minus one more because AColor::Line includes both endpoints - y + t->GetHeight() - kBottomMargin - 1); - } -} - -#if 0 -// now unused -/// This method draws the cursor things, both in the -/// ruler as seen at the top of the screen, but also in each of the -/// selected tracks. -/// These are the 'vertical lines' through waves, notes, and ruler. -void TrackPanel::DrawCursor() -{ - wxClientDC dc( this ); - DoDrawCursor( dc ); -} -#endif - -std::pair TrackPanel::GetCursorRectangle() -{ - if (!mViewInfo->selectedRegion.isPoint()) { - mCursorTime = -1.0; - mNewCursorX = -1; - } - else { - mCursorTime = mViewInfo->selectedRegion.t0(); - mNewCursorX = mViewInfo->TimeToPosition(mCursorTime, GetLeftOffset()); - } - - wxRect rect(mLastCursorX, 0, 1, mBacking->GetHeight()); -#if defined(__WXMAC__) - rect.Inflate(1, 0); -#endif - - return std::make_pair( - rect, - mLastCursorX != mNewCursorX - ); -} - -void TrackPanel::UndrawCursor(wxDC & dc) -{ - bool onScreen; - - if( mLastCursorX != -1 ) - { - int width; - GetTracksUsableArea(&width, NULL); - onScreen = between_incexc(GetLeftOffset(), - mLastCursorX, - GetLeftOffset() + width); - if( onScreen ) -#if defined(__WXMAC__) - // On OSX, if a HiDPI resolution is being used, the line will actually take up - // more than 1 pixel (even though it is drawn as 1), so we restore the surrounding - // pixels as well. (This is because the wxClientDC doesn't know about the scaling.) - dc.Blit(mLastCursorX - 1, 0, 3, mBacking->GetHeight(), &mBackingDC, mLastCursorX - 1, 0); -#else - dc.Blit(mLastCursorX, 0, 1, mBacking->GetHeight(), &mBackingDC, mLastCursorX, 0); -#endif - } -} - -void TrackPanel::DoDrawCursor(wxDC & dc) -{ - mLastCursorX = mNewCursorX; - if (mLastCursorX == -1) - return; - - const bool - onScreen = between_incexc(mViewInfo->h, - mCursorTime, - GetScreenEndTime() ); - - if( !onScreen ) - return; - - AColor::CursorColor(&dc); - - // Draw cursor in all selected tracks - VisibleTrackIterator iter( GetProject() ); - for( Track *t = iter.First(); t; t = iter.Next() ) - { - if( t->GetSelected() || mAx->IsFocused( t ) ) - { - int y = t->GetY() - mViewInfo->vpos; - wxCoord top = y + kTopMargin; - // Minus one more because AColor::Line includes both endpoints - wxCoord bottom = y + t->GetHeight() - kBottomMargin - 1; - - // MB: warp() is not needed here as far as I know, in fact it creates a bug. Removing it fixes that. - AColor::Line(dc, mLastCursorX, top, mLastCursorX, bottom); // <-- The whole point of this routine. - -#ifdef EXPERIMENTAL_OUTPUT_DISPLAY - if(MONO_WAVE_PAN(t)){ - y = t->GetY(true) - mViewInfo->vpos + 1; - top = y + kTopInset; - bottom = y + t->GetHeight(true) - kTopInset; - AColor::Line( dc, mLastCursorX, top, mLastCursorX, bottom ); - } -#endif - - } - } - - // AS: Ah, no, this is where we draw the blinky thing in the ruler. - mRuler->DrawCursor(mCursorTime); - - DisplaySelection(); -} - /// OnSize() is called when the panel is resized void TrackPanel::OnSize(wxSizeEvent & /* event */) { @@ -7890,80 +7639,81 @@ void TrackPanel::DrawOutsideOfTrack(Track * t, wxDC * dc, const wxRect & rect) #endif } +void TrackPanel::AddOverlay(TrackPanelOverlay *pOverlay) +{ + mOverlays.push_back(pOverlay); +} + +bool TrackPanel::RemoveOverlay(TrackPanelOverlay *pOverlay) +{ + const size_t oldSize = mOverlays.size(); + std::remove(mOverlays.begin(), mOverlays.end(), pOverlay); + return oldSize != mOverlays.size(); +} + +void TrackPanel::ClearOverlays() +{ + mOverlays.clear(); +} + void TrackPanel::DrawOverlays(bool repaint) { - // Determine which overlays are outdated. - enum { - n_pairs = -#ifdef EXPERIMENTAL_SCRUBBING_BASIC - 3 -#else - 2 -#endif - }; - std::pair pairs[n_pairs] = { - GetIndicatorRectangle(), - GetCursorRectangle(), -#ifdef EXPERIMENTAL_SCRUBBING_BASIC - GetScrubSpeedRectangle(), -#endif - }; + size_t n_pairs = mOverlays.size(); - { - // Drawing now goes directly to the client area - wxClientDC dc(this); + std::vector< std::pair > pairs; + pairs.reserve(n_pairs); - // See what requires redrawing. If repainting, all. - // If not, then whatever is outdated, and whatever will be damaged by - // undrawing. - // By redrawing only what needs it, we avoid flashing things like - // the cursor that are drawn with xor. - if (!repaint) { - bool done; - do { - done = true; - for (int ii = 0; ii < n_pairs; ++ii) { - for (int jj = ii + 1; jj < n_pairs; ++jj) { - if (pairs[ii].second != pairs[jj].second && - pairs[ii].first.Intersects(pairs[jj].first)) { - done = false; - pairs[ii].second = pairs[jj].second = true; - } + // Find out the rectangles and outdatedness for each overlay + wxSize size(mBackingDC.GetSize()); + for (const auto pOverlay : mOverlays) + pairs.push_back(pOverlay->GetRectangle(size)); + + // See what requires redrawing. If repainting, all. + // If not, then whatever is outdated, and whatever will be damaged by + // undrawing. + // By redrawing only what needs it, we avoid flashing things like + // the cursor that are drawn with invert. + if (!repaint) { + bool done; + do { + done = true; + for (size_t ii = 0; ii < n_pairs; ++ii) { + for (size_t jj = ii + 1; jj < n_pairs; ++jj) { + if (pairs[ii].second != pairs[jj].second && + pairs[ii].first.Intersects(pairs[jj].first)) { + done = false; + pairs[ii].second = pairs[jj].second = true; } } - } while (!done); + } + } while (!done); + } + + // Erase + bool done = true; + auto it2 = pairs.begin(); + for (auto pOverlay : mOverlays) { + if (repaint || it2->second) { + done = false; + wxClientDC dc(this); + pOverlay->Erase(dc, mBackingDC); + } + ++it2; + } + + // Draw + if (!done) { + it2 = pairs.begin(); + for (auto pOverlay : mOverlays) { + if (repaint || it2->second) { + wxClientDC dc(this); + TrackPanelCellIterator begin(this, true); + TrackPanelCellIterator end(this, false); + pOverlay->Draw(dc, begin, end); + } + ++it2; } } - - if (repaint || pairs[0].second) { - wxClientDC dc(this); - UndrawIndicator(dc); - } - if (repaint || pairs[1].second) { - wxClientDC dc(this); - UndrawCursor(dc); - } -#ifdef EXPERIMENTAL_SCRUBBING_BASIC - if (repaint || pairs[2].second) { - wxClientDC dc(this); - UndrawScrubSpeed(dc); - } -#endif - - if (repaint || pairs[0].second) { - wxClientDC dc(this); - DoDrawIndicator(dc); - } - if (repaint || pairs[1].second) { - wxClientDC dc(this); - DoDrawCursor(dc); - } -#ifdef EXPERIMENTAL_SCRUBBING_BASIC - if (repaint || pairs[2].second) { - wxClientDC dc(this); - DoDrawScrubSpeed(dc); - } -#endif } /// Draw a three-level highlight gradient around the focused track. @@ -10036,6 +9786,46 @@ LWSlider * TrackInfo::PanSlider(WaveTrack *t, bool captured) const return captured ? mPanCaptured : mPan; } +TrackPanelCellIterator::TrackPanelCellIterator(TrackPanel *trackPanel, bool begin) + : mPanel(trackPanel) + , mIter(trackPanel->GetProject()) + , mpCell(begin ? mIter.First() : NULL) +{ +} + +TrackPanelCellIterator &TrackPanelCellIterator::operator++ () +{ + mpCell = mIter.Next(); + return *this; +} + +TrackPanelCellIterator TrackPanelCellIterator::operator++ (int) +{ + TrackPanelCellIterator copy(*this); + ++ *this; + return copy; +} + +auto TrackPanelCellIterator::operator* () const -> value_type +{ + if (!mpCell) + return std::make_pair((Track*)nullptr, wxRect()); + + // Convert virtual coordinate to physical + int width; + mPanel->GetTracksUsableArea(&width, NULL); + int y = mpCell->GetY() - mPanel->GetViewInfo()->vpos; + return std::make_pair( + mpCell, + wxRect( + mPanel->GetLeftOffset(), + y + kTopMargin, + width, + mpCell->GetHeight() - (kTopMargin + kBottomMargin) + ) + ); +} + static TrackPanel * TrackPanelFactory(wxWindow * parent, wxWindowID id, const wxPoint & pos, diff --git a/src/TrackPanel.h b/src/TrackPanel.h index e3b316418..e901f47c9 100644 --- a/src/TrackPanel.h +++ b/src/TrackPanel.h @@ -35,6 +35,7 @@ class wxRect; class LabelTrack; class SpectrumAnalyst; class TrackPanel; +class TrackPanelOverlay; class TrackArtist; class Ruler; class SnapManager; @@ -250,18 +251,6 @@ class AUDACITY_DLL_API TrackPanel final : public wxPanel { virtual bool IsOverCutline(WaveTrack * track, wxRect &rect, wxMouseEvent &event); virtual void HandleTrackSpecificMouseEvent(wxMouseEvent & event); - virtual void TimerUpdateIndicator(double playPos); - // Second member of pair indicates whether the indicator is out of date: - virtual std::pair GetIndicatorRectangle(); - virtual void UndrawIndicator(wxDC & dc); - /// draws the green line on the tracks to show playback position - virtual void DoDrawIndicator(wxDC & dc); - - // Second member of pair indicates whether the cursor is out of date: - virtual std::pair GetCursorRectangle(); - virtual void UndrawCursor(wxDC & dc); - virtual void DoDrawCursor(wxDC & dc); - #ifdef EXPERIMENTAL_SCRUBBING_BASIC bool ShouldDrawScrubSpeed(); virtual void TimerUpdateScrubbing(double playPos); @@ -547,6 +536,15 @@ protected: virtual void DrawOutsideOfTrack (Track *t, wxDC* dc, const wxRect & rect); public: + // Register and unregister overlay objects. + // The sequence in which they were registered is the sequence in + // which they are painted. + // TrackPanel is not responsible for their memory management. + virtual void AddOverlay(TrackPanelOverlay *pOverlay); + // Returns true if the overlay was found + virtual bool RemoveOverlay(TrackPanelOverlay *pOverlay); + virtual void ClearOverlays(); + // Erase and redraw things like the cursor, cheaply and directly to the // client area, without full refresh. virtual void DrawOverlays(bool repaint); @@ -597,14 +595,6 @@ protected: TrackPanel *parent; } mTimer; - // This stores the parts of the screen that get overwritten by the indicator - // and cursor - int mLastIndicatorX; - int mNewIndicatorX; - int mLastCursorX; - double mCursorTime; - int mNewCursorX; - // Quick-Play indicator postion int mOldQPIndicatorPos; @@ -854,6 +844,11 @@ protected: TrackPanelAx *mAx; +public: + TrackPanelAx &GetAx() { return *mAx; } + +protected: + wxString mSoloPref; // Keeps track of extra fractional vertical scroll steps @@ -867,6 +862,10 @@ protected: public: wxSize vrulerSize; + protected: + std::vector mOverlays; + + public: DECLARE_EVENT_TABLE() }; diff --git a/src/TrackPanelCell.h b/src/TrackPanelCell.h new file mode 100644 index 000000000..ca5adcde1 --- /dev/null +++ b/src/TrackPanelCell.h @@ -0,0 +1,19 @@ +/********************************************************************** + +Audacity: A Digital Audio Editor + +TrackPanelCell.h + +Paul Licameli + +**********************************************************************/ + +#ifndef __AUDACITY_TRACK_PANEL_CELL__ +#define __AUDACITY_TRACK_PANEL_CELL__ + +// Future: TrackPanelCell will be generalized to a new abstract base class of Track +// and of other things. +class Track; +using TrackPanelCell = Track; + +#endif diff --git a/src/TrackPanelCellIterator.h b/src/TrackPanelCellIterator.h new file mode 100644 index 000000000..bba5fb3af --- /dev/null +++ b/src/TrackPanelCellIterator.h @@ -0,0 +1,52 @@ +/********************************************************************** + +Audacity: A Digital Audio Editor + +TrackPanelCellIterator.h + +Paul Licameli + +**********************************************************************/ + +#ifndef __AUDACITY_TRACK_PANEL_CELL_ITERATOR__ +#define __AUDACITY_TRACK_PANEL_CELL_ITERATOR__ + +#include "Track.h" + +class Track; +typedef Track TrackPanelCell; + +class TrackPanel; + +// A class that allows iteration over the rectangles of visible cells. +class TrackPanelCellIterator +{ +public: + TrackPanelCellIterator(TrackPanel *trackPanel, bool begin); + + // implement the STL iterator idiom + + TrackPanelCellIterator &operator++ (); + TrackPanelCellIterator operator++ (int); + + friend inline bool operator== + (const TrackPanelCellIterator &lhs, const TrackPanelCellIterator &rhs) + { + return lhs.mpCell == rhs.mpCell; + } + + using value_type = std::pair; + value_type operator * () const; + +private: + TrackPanel *mPanel; + VisibleTrackIterator mIter; + TrackPanelCell *mpCell; +}; + +inline bool operator != +(const TrackPanelCellIterator &lhs, const TrackPanelCellIterator &rhs) +{ + return !(lhs == rhs); +} +#endif diff --git a/src/TrackPanelOverlay.cpp b/src/TrackPanelOverlay.cpp new file mode 100644 index 000000000..7179e97b4 --- /dev/null +++ b/src/TrackPanelOverlay.cpp @@ -0,0 +1,40 @@ +/********************************************************************** + +Audacity: A Digital Audio Editor + +TrackPanelOverlay.cpp + +Paul Licameli split from TrackPanel.cpp + +**********************************************************************/ + +#include "TrackPanelOverlay.h" + +#include + +TrackPanelOverlay::~TrackPanelOverlay() +{ +} + +std::pair TrackPanelOverlay::GetRectangle(wxSize size) +{ + auto result = DoGetRectangle(size); +#ifdef __WXMAC__ + // On OSX, if a HiDPI resolution is being used, a vertical line will actually take up + // more than 1 pixel (even though it is drawn as 1), so we restore the surrounding + // pixels as well. (This is because the wxClientDC doesn't know about the scaling. + result.first.Inflate(1, 0); +#endif + return result; +} + +void TrackPanelOverlay::Erase(wxDC &dc, wxDC &src) +{ + wxRect rect(dc.GetSize()); + rect.Intersect(src.GetSize()); + auto smallRect(GetRectangle(src.GetSize()).first); + rect.Intersect(smallRect); + if (!rect.IsEmpty()) + dc.Blit(rect.x, rect.y, rect.width, rect.height, + &src, rect.x, rect.y); +} diff --git a/src/TrackPanelOverlay.h b/src/TrackPanelOverlay.h new file mode 100644 index 000000000..752c0db87 --- /dev/null +++ b/src/TrackPanelOverlay.h @@ -0,0 +1,43 @@ +/********************************************************************** + +Audacity: A Digital Audio Editor + +TrackPanelOverlay.h + +Paul Licameli split from TrackPanel.cpp + +**********************************************************************/ + +#ifndef __AUDACITY_TRACK_PANEL_OVERLAY__ +#define __AUDACITY_TRACK_PANEL_OVERLAY__ + +class TrackPanelCellIterator; + +#include +class wxDC; +class wxRect; +class wxSize; + +class TrackPanelOverlay +{ +public: + virtual ~TrackPanelOverlay() = 0; + + // nonvirtual wrapper + std::pair GetRectangle(wxSize size); + + // size passes the dimensions of the backing dc + // First member of pair is the rectangle that would be erased + // Second member of pair indicates whether the overlay is out of date + virtual std::pair DoGetRectangle(wxSize size) = 0; + + // Default implementation blits from backing store over GetRectangle().first + virtual void Erase(wxDC &dc, wxDC &src); + + // Draw; dc.GetSize() tells you the total dimensions, and the iterators let you + // find the rectangles of tracks (or other sub-rectangles of the panel) + virtual void Draw + (wxDC &dc, TrackPanelCellIterator begin, TrackPanelCellIterator end) = 0; +}; + +#endif diff --git a/src/ViewInfo.cpp b/src/ViewInfo.cpp index 4d3f59fc2..b3ea4db9d 100644 --- a/src/ViewInfo.cpp +++ b/src/ViewInfo.cpp @@ -13,6 +13,7 @@ Paul Licameli #include +#include "AudioIO.h" #include "Internat.h" #include "prefs/GUISettings.h" #include "Prefs.h" @@ -123,6 +124,7 @@ ViewInfo::ViewInfo(double start, double screenDuration, double pixelsPerSecond) , scrollStep(16) , bUpdateTrackIndicator(true) , bScrollBeyondZero(false) + , mRecentStreamTime(-1.0) { UpdatePrefs(); } @@ -176,3 +178,9 @@ bool ViewInfo::ReadXMLAttribute(const wxChar *attr, const wxChar *value) return false; } + +void ViewInfo::OnTimer(wxCommandEvent &event) +{ + mRecentStreamTime = gAudioIO->GetStreamTime(); + event.Skip(); +} diff --git a/src/ViewInfo.h b/src/ViewInfo.h index 116960c55..af21a622d 100644 --- a/src/ViewInfo.h +++ b/src/ViewInfo.h @@ -12,6 +12,7 @@ #define __AUDACITY_VIEWINFO__ #include +#include #include "SelectedRegion.h" @@ -131,10 +132,10 @@ public: // Exclusive: wxInt64 GetFisheyeRightBoundary(wxInt64 WXUNUSED(origin = 0)) const {return 0;} // stub - }; -class AUDACITY_DLL_API ViewInfo final : public ZoomInfo +class AUDACITY_DLL_API ViewInfo final + : public wxEvtHandler, public ZoomInfo { public: ViewInfo(double start, double screenDuration, double pixelsPerSecond); @@ -180,8 +181,15 @@ public: bool bScrollBeyondZero; + // During timer update, grab the volatile stream time just once, so that + // various other drawing code can use the exact same value. + double mRecentStreamTime; + void WriteXMLAttributes(XMLWriter &xmlFile); bool ReadXMLAttribute(const wxChar *attr, const wxChar *value); + + // Receive track panel timer notifications + void OnTimer(wxCommandEvent &event); }; #endif diff --git a/src/tracks/ui/EditCursorOverlay.cpp b/src/tracks/ui/EditCursorOverlay.cpp new file mode 100644 index 000000000..15f147d3f --- /dev/null +++ b/src/tracks/ui/EditCursorOverlay.cpp @@ -0,0 +1,118 @@ +/********************************************************************** + +Audacity: A Digital Audio Editor + +EditCursorOverlay.cpp + +Paul Licameli split from TrackPanel.cpp + +**********************************************************************/ + +#include "../../Audacity.h" +#include "EditCursorOverlay.h" +#include "../../Experimental.h" + +#include "../../AColor.h" +#include "../../widgets/Ruler.h" +#include "../../Project.h" +//#include "../../TrackPanel.h" +#include "../../TrackPanelCell.h" +#include "../../TrackPanelCellIterator.h" +#include "../../TrackPanelAx.h" +#include "../../ViewInfo.h" + +#include + +namespace { + template < class LOW, class MID, class HIGH > + bool between_incexc(LOW l, MID m, HIGH h) + { + return (m >= l && m < h); + } +} + +EditCursorOverlay::EditCursorOverlay(AudacityProject *project) + : mProject(project) + , mLastCursorX(-1) + , mCursorTime(-1) + , mNewCursorX(-1) +{ +} + +EditCursorOverlay::~EditCursorOverlay() +{ +} + +std::pair EditCursorOverlay::DoGetRectangle(wxSize size) +{ + const SelectedRegion &selection = mProject->GetSelection(); + if (!selection.isPoint()) { + mCursorTime = -1.0; + mNewCursorX = -1; + } + else { + mCursorTime = selection.t0(); + mNewCursorX = mProject->GetZoomInfo().TimeToPosition + (mCursorTime, mProject->GetTrackPanel()->GetLeftOffset()); + } + + return std::make_pair( + mLastCursorX == -1 + ? wxRect() + : wxRect(mLastCursorX, 0, 1, size.GetHeight()), + mLastCursorX != mNewCursorX + ); +} + + +void EditCursorOverlay::Draw + (wxDC &dc, TrackPanelCellIterator begin, TrackPanelCellIterator end) +{ + mLastCursorX = mNewCursorX; + if (mLastCursorX == -1) + return; + + const ZoomInfo &viewInfo = mProject->GetZoomInfo(); + + const bool + onScreen = between_incexc(viewInfo.h, + mCursorTime, + mProject->GetScreenEndTime()); + + if (!onScreen) + return; + + AColor::CursorColor(&dc); + + // Draw cursor in all selected tracks + for (; begin != end; ++begin) + { + TrackPanelCellIterator::value_type data(*begin); + Track *const pTrack = data.first; + if (!pTrack) + continue; + if (pTrack->GetSelected() || + mProject->GetTrackPanel()->GetAx().IsFocused(pTrack)) + { + const wxRect &rect = data.second; + // AColor::Line includes both endpoints so use GetBottom() + AColor::Line(dc, mLastCursorX, rect.GetTop(), mLastCursorX, rect.GetBottom()); // <-- The whole point of this routine. + +#ifdef EXPERIMENTAL_OUTPUT_DISPLAY + if (MONO_WAVE_PAN(t)){ + y = t->GetY(true) - mViewInfo->vpos + 1; + top = y + kTopInset; + bottom = y + t->GetHeight(true) - kTopInset; + AColor::Line(dc, mLastCursorX, top, mLastCursorX, bottom); + } +#endif + + } + } + + // AS: Ah, no, this is where we draw the blinky thing in the ruler. + mProject->GetRulerPanel()->DrawCursor(mCursorTime); + + // This updates related displays such as numbers on the status bar + mProject->TP_DisplaySelection(); +} diff --git a/src/tracks/ui/EditCursorOverlay.h b/src/tracks/ui/EditCursorOverlay.h new file mode 100644 index 000000000..2c13ec75b --- /dev/null +++ b/src/tracks/ui/EditCursorOverlay.h @@ -0,0 +1,36 @@ +/********************************************************************** + +Audacity: A Digital Audio Editor + +EditCursorOverlay.h + +Paul Licameli split from TrackPanel.cpp + +**********************************************************************/ + +#ifndef __AUDACITY_EDIT_CURSOR_OVERLAY__ +#define __AUDACITY_EDIT_CURSOR_OVERLAY__ + +#include "../../TrackPanelOverlay.h" + +class AudacityProject; + +class EditCursorOverlay final : public TrackPanelOverlay +{ +public: + EditCursorOverlay(AudacityProject *project); + virtual ~EditCursorOverlay(); + +private: + std::pair DoGetRectangle(wxSize size) override; + void Draw + (wxDC &dc, TrackPanelCellIterator begin, TrackPanelCellIterator end) override; + + AudacityProject *mProject; + + int mLastCursorX; + double mCursorTime; + int mNewCursorX; +}; + +#endif diff --git a/src/tracks/ui/PlayIndicatorOverlay.cpp b/src/tracks/ui/PlayIndicatorOverlay.cpp new file mode 100644 index 000000000..1359b84c2 --- /dev/null +++ b/src/tracks/ui/PlayIndicatorOverlay.cpp @@ -0,0 +1,151 @@ +/********************************************************************** + +Audacity: A Digital Audio Editor + +PlayIndicatorOverlay.cpp + +Paul Licameli split from TrackPanel.cpp + +**********************************************************************/ + +#include "PlayIndicatorOverlay.h" + +#include "../../AColor.h" +#include "../../AudioIO.h" +#include "../../Project.h" +#include "../../TrackPanel.h" +#include "../../TrackPanelCell.h" +#include "../../TrackPanelCellIterator.h" +#include "../../widgets/Ruler.h" + +#include + +#include + +namespace { + template < class LOW, class MID, class HIGH > + bool between_incexc(LOW l, MID m, HIGH h) + { + return (m >= l && m < h); + } +} + +PlayIndicatorOverlay::PlayIndicatorOverlay(AudacityProject *project) + : mProject(project) + , mLastIndicatorX(-1) + , mNewIndicatorX(-1) +{ + mProject->Connect(EVT_TRACK_PANEL_TIMER, + wxCommandEventHandler(PlayIndicatorOverlay::OnTimer), + NULL, + this); +} + +PlayIndicatorOverlay::~PlayIndicatorOverlay() +{ + mProject->Disconnect(EVT_TRACK_PANEL_TIMER, + wxCommandEventHandler(PlayIndicatorOverlay::OnTimer), + NULL, + this); +} + +std::pair PlayIndicatorOverlay::DoGetRectangle(wxSize size) +{ + wxRect rect(mLastIndicatorX, 0, 1, size.GetHeight()); + return std::make_pair( + rect, + mLastIndicatorX != mNewIndicatorX + ); +} + + +void PlayIndicatorOverlay::Draw + (wxDC &dc, TrackPanelCellIterator begin, TrackPanelCellIterator end) +{ + mLastIndicatorX = mNewIndicatorX; + if (!between_incexc(0, mLastIndicatorX, dc.GetSize().GetWidth())) + return; + + const ZoomInfo &viewInfo = mProject->GetZoomInfo(); + TrackPanel *const trackPanel = mProject->GetTrackPanel(); + + double pos = viewInfo.PositionToTime(mLastIndicatorX, trackPanel->GetLeftOffset()); + + // Set play/record color + bool rec = (gAudioIO->GetNumCaptureChannels() > 0); + AColor::IndicatorColor(&dc, !rec); + + mProject->GetRulerPanel()->DrawIndicator(pos, rec); + + // Draw indicator in all visible tracks + for (; begin != end; ++begin) + { + TrackPanelCellIterator::value_type data(*begin); + Track *const pTrack = data.first; + if (!pTrack) + continue; + + // Don't draw the indicator in label tracks + if (pTrack->GetKind() == Track::Label) + { + continue; + } + + // Draw the NEW indicator in its NEW location + // AColor::Line includes both endpoints so use GetBottom() + const wxRect &rect = data.second; + AColor::Line(dc, + mLastIndicatorX, + rect.GetTop(), + mLastIndicatorX, + rect.GetBottom()); + } +} + +void PlayIndicatorOverlay::Erase(wxDC &dc, wxDC &src) +{ + TrackPanelOverlay::Erase(dc, src); + mProject->GetRulerPanel()->ClearIndicator(); +} + +void PlayIndicatorOverlay::OnTimer(wxCommandEvent &event) +{ + // Let other listeners get the notification + event.Skip(); + + if (!mProject->IsAudioActive()) + mNewIndicatorX = -1; + else { + ViewInfo &viewInfo = mProject->GetViewInfo(); + + // Calculate the horizontal position of the indicator + const double playPos = viewInfo.mRecentStreamTime; + + const bool onScreen = playPos >= 0.0 && + between_incexc(viewInfo.h, + playPos, + mProject->GetScreenEndTime()); + + // This displays the audio time, too... + mProject->TP_DisplaySelection(); + + // BG: Scroll screen if option is set + // msmeyer: But only if not playing looped or in one-second mode + if (viewInfo.bUpdateTrackIndicator && + mProject->mLastPlayMode != loopedPlay && + mProject->mLastPlayMode != oneSecondPlay && + playPos >= 0 && + !onScreen && + !gAudioIO->IsPaused()) + { + mProject->TP_ScrollWindow(playPos); + } + + // Always update scrollbars even if not scrolling the window. This is + // important when NEW audio is recorded, because this can change the + // length of the project and therefore the appearance of the scrollbar. + mProject->TP_RedrawScrollbars(); + + mNewIndicatorX = viewInfo.TimeToPosition(playPos, mProject->GetTrackPanel()->GetLeftOffset()); + } +} diff --git a/src/tracks/ui/PlayIndicatorOverlay.h b/src/tracks/ui/PlayIndicatorOverlay.h new file mode 100644 index 000000000..643526946 --- /dev/null +++ b/src/tracks/ui/PlayIndicatorOverlay.h @@ -0,0 +1,40 @@ +/********************************************************************** + +Audacity: A Digital Audio Editor + +PlayIndicatorOverlay.h + +Paul Licameli split from TrackPanel.cpp + +**********************************************************************/ + +#ifndef __AUDACITY_PLAY_INDICATOR_OVERLAY__ +#define __AUDACITY_PLAY_INDICATOR_OVERLAY__ + +#include "../../TrackPanelOverlay.h" +#include + +class AudacityProject; + + +class PlayIndicatorOverlay final : public wxEvtHandler, public TrackPanelOverlay +{ +public: + PlayIndicatorOverlay(AudacityProject *project); + virtual ~PlayIndicatorOverlay(); + +private: + std::pair DoGetRectangle(wxSize size) override; + void Draw + (wxDC &dc, TrackPanelCellIterator begin, TrackPanelCellIterator end) override; + void Erase(wxDC &dc, wxDC &src) override; + + void OnTimer(wxCommandEvent &event); + + + AudacityProject *mProject; + int mLastIndicatorX; + int mNewIndicatorX; +}; + +#endif diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp new file mode 100644 index 000000000..4a262b545 --- /dev/null +++ b/src/tracks/ui/Scrubbing.cpp @@ -0,0 +1,53 @@ +/********************************************************************** + +Audacity: A Digital Audio Editor + +Scrubbing.cpp + +Paul Licameli split from TrackPanel.cpp + +**********************************************************************/ + +#include "Scrubbing.h" + +#include "../../Project.h" +#include "../../TrackPanel.h" +#include "../../TrackPanelCell.h" +#include "../../TrackPanelCellIterator.h" + +#include + +ScrubbingOverlay::ScrubbingOverlay(AudacityProject *project) + : mProject(project) +{ + mProject->Connect(EVT_TRACK_PANEL_TIMER, + wxCommandEventHandler(ScrubbingOverlay::OnTimer), + NULL, + this); +} + +ScrubbingOverlay::~ScrubbingOverlay() +{ + mProject->Disconnect(EVT_TRACK_PANEL_TIMER, + wxCommandEventHandler(ScrubbingOverlay::OnTimer), + NULL, + this); +} + +std::pair ScrubbingOverlay::DoGetRectangle(wxSize) +{ + return std::make_pair(wxRect(), false); +} + +void ScrubbingOverlay::Draw + (wxDC &dc, TrackPanelCellIterator begin, TrackPanelCellIterator end) +{ +} + +void ScrubbingOverlay::OnTimer(wxCommandEvent &event) +{ + // Let other listeners get the notification + event.Skip(); + + // To do: move code here +} diff --git a/src/tracks/ui/Scrubbing.h b/src/tracks/ui/Scrubbing.h new file mode 100644 index 000000000..c6c60ac71 --- /dev/null +++ b/src/tracks/ui/Scrubbing.h @@ -0,0 +1,36 @@ +/********************************************************************** + +Audacity: A Digital Audio Editor + +Scrubbing.h + +Paul Licameli split from TrackPanel.cpp + +**********************************************************************/ + +#ifndef __AUDACITY_SCRUBBING__ +#define __AUDACITY_SCRUBBING__ + +#include + +#include "../../TrackPanelOverlay.h" + +class AudacityProject; + +class ScrubbingOverlay : public wxEvtHandler, public TrackPanelOverlay +{ +public: + ScrubbingOverlay(AudacityProject *project); + virtual ~ScrubbingOverlay(); + +private: + std::pair DoGetRectangle(wxSize size) override; + void Draw + (wxDC &dc, TrackPanelCellIterator begin, TrackPanelCellIterator end) override; + + void OnTimer(wxCommandEvent &event); + + AudacityProject *mProject; +}; + +#endif diff --git a/win/Projects/Audacity/Audacity.vcxproj b/win/Projects/Audacity/Audacity.vcxproj index a95af18d5..923d36a64 100755 --- a/win/Projects/Audacity/Audacity.vcxproj +++ b/win/Projects/Audacity/Audacity.vcxproj @@ -1,4 +1,4 @@ - + @@ -213,6 +213,10 @@ + + + + @@ -435,7 +439,13 @@ + + + + + + @@ -1097,4 +1107,4 @@ - \ No newline at end of file + diff --git a/win/Projects/Audacity/Audacity.vcxproj.filters b/win/Projects/Audacity/Audacity.vcxproj.filters index 6ecb3975e..aea512d60 100755 --- a/win/Projects/Audacity/Audacity.vcxproj.filters +++ b/win/Projects/Audacity/Audacity.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -69,6 +69,12 @@ {99325e00-3930-4e03-a599-999f275db78e} + + {7b403d1f-de11-43d5-becc-d9d118d06f18} + + + {aa9627ea-e614-4704-bf68-4a347023569f} + @@ -275,9 +281,6 @@ src - - src - src @@ -857,6 +860,21 @@ src + + src + + + src\tracks\ui + + + src\tracks\ui + + + src\tracks\ui + + + src + @@ -1714,6 +1732,9 @@ src\effects\VST + + src + src @@ -1732,6 +1753,21 @@ src + + src + + + src\tracks\ui + + + src\tracks\ui + + + src\tracks\ui + + + src + @@ -1952,4 +1988,4 @@ plug-ins - \ No newline at end of file +