From 9ba6e03bd42bb6435a1e08131cfb749233d23335 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 13 Aug 2015 10:59:42 -0400 Subject: [PATCH] Reorganize code for drawing cursor and indicator, so it can generalize to... ... other things (like scrub speed) --- src/TrackPanel.cpp | 279 +++++++++++++++++++++++++++------------------ src/TrackPanel.h | 26 +++-- 2 files changed, 187 insertions(+), 118 deletions(-) diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index e345d29f8..38beea850 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -484,7 +484,6 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id, UpdatePrefs(); mRedrawAfterStop = false; - mIndicatorShowing = false; mPencilCursor = MakeCursor( wxCURSOR_PENCIL, DrawCursorXpm, 12, 22); mSelectCursor = MakeCursor( wxCURSOR_IBEAM, IBeamCursorXpm, 17, 16); @@ -565,8 +564,9 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id, mSnapLeft = -1; mSnapRight = -1; - mLastCursorX = -1; - mLastIndicatorX = -1; + mLastCursorX = mNewCursorX = -1; + mLastIndicatorX = mNewIndicatorX = -1; + mCursorTime = -1.0; mOldQPIndicatorPos = -1; // Register for tracklist updates @@ -1106,20 +1106,8 @@ void TrackPanel::OnTimer() DisplaySelection(); } - // 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.) - - if (!gAudioIO->IsPaused() && // Don't redraw paused indicator needlessly for timer - ( mIndicatorShowing || IsAudioActive()) - -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL - && !mSmoothScrollingScrub -#endif - ) - { - DrawIndicator(); - } + TimerUpdateIndicator(); + DrawOverlays(false); if(IsAudioActive() && gAudioIO->GetNumCaptureChannels()) { @@ -1182,6 +1170,8 @@ void TrackPanel::ScrollDuringDrag() } } +#if 0 +// now unused /// This updates the indicator (on a timer tick) that shows /// where the current play or record position is. To do this, /// we cheat a little. The indicator is drawn during the ruler @@ -1194,6 +1184,7 @@ void TrackPanel::DrawIndicator() wxClientDC dc( this ); DoDrawIndicator( dc ); } +#endif void TrackPanel::DrawQuickPlayIndicator(wxDC & dc, double pos) { @@ -1225,53 +1216,31 @@ void TrackPanel::DrawQuickPlayIndicator(wxDC & dc, double pos) } } -/// Second level DrawIndicator() -void TrackPanel::DoDrawIndicator - (wxDC & dc, bool repairOld /* = false */, double indicator /* = -1 */) +void TrackPanel::TimerUpdateIndicator() { - bool onScreen; - double pos; + double pos = 0.0; - if (!repairOld) - { - // Erase the old indicator. - if (mLastIndicatorX != -1) - { - onScreen = between_inclusive(GetLeftOffset(), - mLastIndicatorX, - GetLeftOffset() + mViewInfo->GetScreenWidth()); - 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; - } + if (!IsAudioActive()) + mNewIndicatorX = -1; + else { + // Calculate the horizontal position of the indicator + pos = gAudioIO->GetStreamTime(); - dc.Blit(mLastIndicatorX, 0, 1, mBacking->GetHeight(), &mBackingDC, mLastIndicatorX, 0); - } - - // Nothing's ever perfect... - // - // Redraw the cursor since we may have just wiped it out - if (mLastCursorX == mLastIndicatorX) - { - DoDrawCursor( dc ); - } +#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL + if (mSmoothScrollingScrub) { + // Pan the view, so that we center the play indicator. + mViewInfo->h = pos - mViewInfo->screen / 2.0; + if (!mScrollBeyondZero) + // Can't scroll too far left + mViewInfo->h = std::max(0.0, mViewInfo->h); + Refresh(false); } - - pos = indicator; - if (pos < 0.0) - // The stream time can be < 0 if the audio is currently stopped - pos = gAudioIO->GetStreamTime(); - +#endif AudacityProject *p = GetProject(); - bool audioActive = IsAudioActive(); - onScreen = between_inclusive( mViewInfo->h, - pos, - mViewInfo->h + mViewInfo->screen ); + const bool + onScreen = between_inclusive(mViewInfo->h, + pos, + mViewInfo->h + mViewInfo->screen); // This displays the audio time, too... DisplaySelection(); @@ -1281,7 +1250,6 @@ void TrackPanel::DoDrawIndicator if( mViewInfo->bUpdateTrackIndicator && p->mLastPlayMode != loopedPlay && p->mLastPlayMode != oneSecondPlay && - audioActive && pos >= 0 && !onScreen && !gAudioIO->IsPaused() ) @@ -1294,13 +1262,55 @@ void TrackPanel::DoDrawIndicator // length of the project and therefore the appearance of the scrollbar. MakeParentRedrawScrollbars(); - mIndicatorShowing = ( onScreen && audioActive ); - - // Calculate the horizontal position of the indicator - mLastIndicatorX = mViewInfo->TimeToPosition(pos, GetLeftOffset()); + mNewIndicatorX = mViewInfo->TimeToPosition(pos, GetLeftOffset()); } - else - pos = mViewInfo->PositionToTime(mLastIndicatorX, GetLeftOffset()); +} + +std::pair TrackPanel::GetIndicatorRectangle() +{ + return std::make_pair( + wxRect(mLastIndicatorX, 0, 1, mBacking->GetHeight()), + 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) + { + const bool + onScreen = between_inclusive(GetLeftOffset(), + mLastIndicatorX, + GetLeftOffset() + mViewInfo->GetScreenWidth()); + 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; + } + + dc.Blit(mLastIndicatorX, 0, 1, mBacking->GetHeight(), &mBackingDC, mLastIndicatorX, 0); + } + + 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); @@ -1340,6 +1350,8 @@ void TrackPanel::DoDrawIndicator } } +#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. @@ -1349,12 +1361,27 @@ void TrackPanel::DrawCursor() wxClientDC dc( this ); DoDrawCursor( dc ); } +#endif -/// Second level DrawCursor() -void TrackPanel::DoDrawCursor(wxDC & dc) +std::pair TrackPanel::GetCursorRectangle() { - DisableAntialiasing(dc); + if (!mViewInfo->selectedRegion.isPoint()) { + mCursorTime = -1.0; + mNewCursorX = -1; + } + else { + mCursorTime = mViewInfo->selectedRegion.t0(); + mNewCursorX = mViewInfo->TimeToPosition(mCursorTime, GetLeftOffset()); + } + return std::make_pair( + wxRect(mLastCursorX, 0, 1, mBacking->GetHeight()), + mLastCursorX != mNewCursorX + ); +} + +void TrackPanel::UndrawCursor(wxDC & dc) +{ bool onScreen; if( mLastCursorX != -1 ) @@ -1365,18 +1392,23 @@ void TrackPanel::DoDrawCursor(wxDC & dc) if( onScreen ) dc.Blit(mLastCursorX, 0, 1, mBacking->GetHeight(), &mBackingDC, mLastCursorX, 0); } +} - const double time = mViewInfo->selectedRegion.t0(); - mLastCursorX = mViewInfo->TimeToPosition(time, GetLeftOffset()); +void TrackPanel::DoDrawCursor(wxDC & dc) +{ + mLastCursorX = mNewCursorX; + if (mLastCursorX == -1) + return; + const bool onScreen = between_inclusive( mViewInfo->h, - time, + mCursorTime, mViewInfo->h + mViewInfo->screen ); if( !onScreen ) return; - AColor::CursorColor( &dc ); + AColor::CursorColor(&dc); // Draw cursor in all selected tracks VisibleTrackIterator iter( GetProject() ); @@ -1404,7 +1436,7 @@ void TrackPanel::DoDrawCursor(wxDC & dc) } // AS: Ah, no, this is where we draw the blinky thing in the ruler. - mRuler->DrawCursor( time ); + mRuler->DrawCursor(mCursorTime); DisplaySelection(); } @@ -1462,8 +1494,6 @@ void TrackPanel::OnPaint(wxPaintEvent & /* event */) // Retrieve the damage rectangle wxRect box = GetUpdateRegion().GetBox(); - double indicator = -1; - // Recreate the backing bitmap if we have a full refresh // (See TrackPanel::Refresh()) if (mRefreshBacking || (box == GetRect())) @@ -1471,22 +1501,6 @@ void TrackPanel::OnPaint(wxPaintEvent & /* event */) // Reset (should a mutex be used???) mRefreshBacking = false; -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL - if (mSmoothScrollingScrub && IsAudioActive()) { - // Pan the view, so that we center the play indicator. - // By the time DoDrawIndicator() is reached, gAudioIO->GetStreamTime() - // may be a little different. - // Cause DoDrawIndicator to show the value as it was at this time when we begin - // the drawing of the tracks. This prevents flashing of the indicator - // at higher magnifications, and keeps the green line still in the middle. - indicator = gAudioIO->GetStreamTime(); - mViewInfo->h = indicator - mViewInfo->screen / 2.0; - if (!mScrollBeyondZero) - // Can't scroll too far left - mViewInfo->h = std::max(0.0, mViewInfo->h); - } -#endif - // Redraw the backing bitmap DrawTracks(&mBackingDC); @@ -1503,23 +1517,7 @@ void TrackPanel::OnPaint(wxPaintEvent & /* event */) delete dc; // Drawing now goes directly to the client area - wxClientDC cdc(this); - DisableAntialiasing(cdc); - - // Update the indicator in case it was damaged if this project is playing - - if (// !gAudioIO->IsPaused() && // Bug1139: do repaint the line even if paused. - (mIndicatorShowing || IsAudioActive())) - { - // If not smooth scrolling, then - // we just want to repair, not update the old, so set the second param to true. - // This is important because this onPaint could be for just some of the tracks. - DoDrawIndicator(cdc, (indicator < 0), indicator); - } - - // Draw the cursor - if (mViewInfo->selectedRegion.isPoint()) - DoDrawCursor(cdc); + DrawOverlays(true); #if DEBUG_DRAW_TIMING sw.Pause(); @@ -7592,6 +7590,67 @@ void TrackPanel::DrawOutsideOfTrack(Track * t, wxDC * dc, const wxRect & rect) #endif } +void TrackPanel::DrawOverlays(bool repaint) +{ + bool isOutdated = false; + + // Determine which overlays are outdated. + enum { n_pairs = 2 }; + std::pair pairs[n_pairs] = { + GetIndicatorRectangle(), + GetCursorRectangle(), + }; + + { + // Drawing now goes directly to the client area + wxClientDC dc(this); + DisableAntialiasing(dc); + + // 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; + } + } + } + } while (!done); + } + } + + if (repaint || pairs[0].second) { + wxClientDC dc(this); + DisableAntialiasing(dc); + UndrawIndicator(dc); + } + if (repaint || pairs[1].second) { + wxClientDC dc(this); + DisableAntialiasing(dc); + UndrawCursor(dc); + } + + if (repaint || pairs[0].second) { + wxClientDC dc(this); + DisableAntialiasing(dc); + DoDrawIndicator(dc); + } + if (repaint || pairs[1].second) { + wxClientDC dc(this); + DisableAntialiasing(dc); + DoDrawCursor(dc); + } +} + /// Draw a three-level highlight gradient around the focused track. void TrackPanel::HighlightFocusedTrack(wxDC * dc, const wxRect & rect) { @@ -8153,7 +8212,7 @@ void TrackPanel::SeekLeftOrRight mViewInfo->selectedRegion.collapseToT0(); // Move the visual cursor - DrawCursor(); + DrawOverlays(false); } else { diff --git a/src/TrackPanel.h b/src/TrackPanel.h index 3fcd80dae..2cc6db4d3 100644 --- a/src/TrackPanel.h +++ b/src/TrackPanel.h @@ -237,7 +237,7 @@ class AUDACITY_DLL_API TrackPanel:public wxPanel { * initial items. * * Ensures that all pop-down menus start with Name, and the commands for moving - * the track around, via a single set of code. + * the track around, via a single set of c ode. * @param menu the menu to add the commands to. */ virtual void BuildCommonDropMenuItems(wxMenu * menu); @@ -250,12 +250,17 @@ class AUDACITY_DLL_API TrackPanel:public wxPanel { virtual bool HandleTrackLocationMouseEvent(WaveTrack * track, wxRect &rect, wxMouseEvent &event); virtual bool IsOverCutline(WaveTrack * track, wxRect &rect, wxMouseEvent &event); virtual void HandleTrackSpecificMouseEvent(wxMouseEvent & event); - virtual void DrawIndicator(); + + virtual void TimerUpdateIndicator(); + // 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 - /// @param repairOld if true the playback position is not updated/erased, and simply redrawn - /// @param indicator if nonnegative, overrides the indicator value obtainable from AudioIO - virtual void DoDrawIndicator(wxDC & dc, bool repairOld = false, double indicator = -1); - virtual void DrawCursor(); + 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); virtual void ScrollDuringDrag(); @@ -540,6 +545,10 @@ protected: virtual void DrawBordersAroundTrack(Track *t, wxDC* dc, const wxRect & rect, const int labelw, const int vrul); virtual void DrawOutsideOfTrack (Track *t, wxDC* dc, const wxRect & rect); + // Erase and redraw things like the cursor, cheaply and directly to the + // client area, without full refresh. + virtual void DrawOverlays(bool repaint); + virtual int IdOfRate( int rate ); virtual int IdOfFormat( int format ); @@ -581,7 +590,10 @@ protected: // 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 double mOldQPIndicatorPos; @@ -664,8 +676,6 @@ protected: bool mRedrawAfterStop; - bool mIndicatorShowing; - wxMouseEvent mLastMouseEvent; int mMouseClickX;