From aa9481f52e849eb5414bd06372a63a87e71eaed1 Mon Sep 17 00:00:00 2001 From: micpap25 Date: Sun, 3 Jul 2022 18:23:38 -0400 Subject: [PATCH 01/17] Split update across classes and make it polymorphic within Ruler Rework update so that it is polymorphic. Break it into multiple files and allow the ruler's creator to set the updater with a unique pointer creator. --- src/AdornedRulerPanel.cpp | 6 +- src/CMakeLists.txt | 8 + src/FreqWindow.cpp | 12 +- src/TimeTrack.cpp | 5 +- src/effects/Equalization.cpp | 10 +- .../wavetrack/ui/SpectrumVRulerControls.cpp | 6 +- .../wavetrack/ui/WaveformVRulerControls.cpp | 5 +- .../timetrack/ui/TimeTrackVRulerControls.cpp | 7 +- src/widgets/CustomUpdater.cpp | 32 + src/widgets/CustomUpdater.h | 27 + src/widgets/LinearUpdater.cpp | 180 ++++ src/widgets/LinearUpdater.h | 27 + src/widgets/LogarithmicUpdater.cpp | 121 +++ src/widgets/LogarithmicUpdater.h | 28 + src/widgets/Ruler.cpp | 951 +----------------- src/widgets/Ruler.h | 22 +- src/widgets/Updater.cpp | 552 ++++++++++ src/widgets/Updater.h | 84 ++ 18 files changed, 1159 insertions(+), 924 deletions(-) create mode 100644 src/widgets/CustomUpdater.cpp create mode 100644 src/widgets/CustomUpdater.h create mode 100644 src/widgets/LinearUpdater.cpp create mode 100644 src/widgets/LinearUpdater.h create mode 100644 src/widgets/LogarithmicUpdater.cpp create mode 100644 src/widgets/LogarithmicUpdater.h create mode 100644 src/widgets/Updater.cpp create mode 100644 src/widgets/Updater.h diff --git a/src/AdornedRulerPanel.cpp b/src/AdornedRulerPanel.cpp index 5d16b098c35f..aadcdbe1542d 100644 --- a/src/AdornedRulerPanel.cpp +++ b/src/AdornedRulerPanel.cpp @@ -55,6 +55,8 @@ #include "widgets/AButton.h" #include "widgets/AudacityMessageBox.h" #include "widgets/Grabber.h" +#include "widgets/LinearUpdater.h" +#include "widgets/LogarithmicUpdater.h" #include "widgets/wxWidgetsWindowPlacement.h" #include @@ -1280,7 +1282,7 @@ AdornedRulerPanel::AdornedRulerPanel(AudacityProject* project, mOuter = GetClientRect(); - mRuler.SetUseZoomInfo(mLeftOffset, mViewInfo); + mRuler.SetUpdater( std::make_unique( mRuler, mViewInfo ), mLeftOffset ); mRuler.SetLabelEdges( false ); mRuler.SetFormat( Ruler::TimeFormat ); @@ -2577,7 +2579,7 @@ int AdornedRulerPanel::GetRulerHeight(bool showScrubBar) void AdornedRulerPanel::SetLeftOffset(int offset) { mLeftOffset = offset; - mRuler.SetUseZoomInfo(offset, mViewInfo); + mRuler.SetUpdater( std::make_unique( mRuler, mViewInfo ), offset ); } // Draws the scrubbing/seeking indicator. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c2262b211e43..a2b8d84d18d7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1013,6 +1013,8 @@ list( APPEND SOURCES widgets/BackedPanel.h widgets/BasicMenu.cpp widgets/BasicMenu.h + widgets/CustomUpdater.cpp + widgets/CustomUpdater.h widgets/ErrorDialog.cpp widgets/ErrorDialog.h widgets/ExpandingToolBar.cpp @@ -1045,6 +1047,10 @@ list( APPEND SOURCES widgets/ImageRoll.h widgets/KeyView.cpp widgets/KeyView.h + widgets/LinearUpdater.cpp + widgets/LinearUpdater.h + widgets/LogarithmicUpdater.cpp + widgets/LogarithmicUpdater.h widgets/MeterPanel.cpp widgets/MeterPanel.h widgets/MeterPanelBase.cpp @@ -1070,6 +1076,8 @@ list( APPEND SOURCES > widgets/UnwritableLocationErrorDialog.cpp widgets/UnwritableLocationErrorDialog.h + widgets/Updater.cpp + widgets/Updater.h widgets/VetoDialogHook.h widgets/Warning.cpp widgets/Warning.h diff --git a/src/FreqWindow.cpp b/src/FreqWindow.cpp index 335df93968cf..071ec4397922 100644 --- a/src/FreqWindow.cpp +++ b/src/FreqWindow.cpp @@ -83,6 +83,8 @@ the mouse around. #include "./widgets/HelpSystem.h" #include "widgets/AudacityMessageBox.h" #include "widgets/Ruler.h" +#include "widgets/LinearUpdater.h" +#include "widgets/LogarithmicUpdater.h" #include "widgets/VetoDialogHook.h" #if wxUSE_ACCESSIBILITY @@ -674,10 +676,10 @@ void FrequencyPlotDialog::DrawPlot() if (!mData || mDataLen < mWindowSize || mAnalyst->GetProcessedSize() == 0) { wxMemoryDC memDC; - vRuler->ruler.SetLog(false); + vRuler->ruler.SetUpdater(std::make_unique(vRuler->ruler, nullptr)); vRuler->ruler.SetRange(0.0, -dBRange); - hRuler->ruler.SetLog(false); + hRuler->ruler.SetUpdater(std::make_unique(hRuler->ruler, nullptr)); hRuler->ruler.SetRange(0, 1); DrawBackground(memDC); @@ -752,19 +754,19 @@ void FrequencyPlotDialog::DrawPlot() if (mLogAxis) { xStep = pow(2.0f, (log(xRatio) / log(2.0f)) / width); - hRuler->ruler.SetLog(true); + hRuler->ruler.SetUpdater(std::make_unique(hRuler->ruler, nullptr)); } else { xStep = (xMax - xMin) / width; - hRuler->ruler.SetLog(false); + hRuler->ruler.SetUpdater(std::make_unique(hRuler->ruler, nullptr)); } hRuler->ruler.SetUnits(XO("Hz")); } else { xMin = 0; xMax = mAnalyst->GetProcessedSize() / mRate; xStep = (xMax - xMin) / width; - hRuler->ruler.SetLog(false); + hRuler->ruler.SetUpdater(std::make_unique(hRuler->ruler, nullptr)); /* i18n-hint: short form of 'seconds'.*/ hRuler->ruler.SetUnits(XO("s")); } diff --git a/src/TimeTrack.cpp b/src/TimeTrack.cpp index bc80c566b2c0..6cd433ac57f8 100644 --- a/src/TimeTrack.cpp +++ b/src/TimeTrack.cpp @@ -22,6 +22,7 @@ #include #include #include "widgets/Ruler.h" +#include "widgets/LinearUpdater.h" #include "Envelope.h" #include "Mix.h" #include "Project.h" @@ -74,7 +75,7 @@ void TimeTrack::CleanState() SetName(GetDefaultName()); mRuler = std::make_unique(); - mRuler->SetUseZoomInfo(0, mZoomInfo); + mRuler->SetUpdater(std::make_unique(*(mRuler.get()), mZoomInfo), 0); mRuler->SetLabelEdges(false); mRuler->SetFormat(Ruler::TimeFormat); } @@ -102,7 +103,7 @@ TimeTrack::TimeTrack(const TimeTrack &orig, ProtectedCreationArg &&a, ///@TODO: Give Ruler:: a copy-constructor instead of this? mRuler = std::make_unique(); - mRuler->SetUseZoomInfo(0, mZoomInfo); + mRuler->SetUpdater(std::make_unique(*(mRuler.get()), mZoomInfo), 0); mRuler->SetLabelEdges(false); mRuler->SetFormat(Ruler::TimeFormat); } diff --git a/src/effects/Equalization.cpp b/src/effects/Equalization.cpp index 946813a8ef84..e49463a475d2 100644 --- a/src/effects/Equalization.cpp +++ b/src/effects/Equalization.cpp @@ -103,6 +103,8 @@ #include "../WaveClip.h" #include "ViewInfo.h" #include "../WaveTrack.h" +#include "../widgets/LinearUpdater.h" +#include "../widgets/LogarithmicUpdater.h" #include "../widgets/Ruler.h" #include "../widgets/AudacityTextEntryDialog.h" #include "XMLFileReader.h" @@ -2288,7 +2290,7 @@ void EffectEqualization::UpdateDraw() { EnvLogToLin(); mEnvelope = mLinEnvelope.get(); - mFreqRuler->ruler.SetLog(false); + mFreqRuler->ruler.SetUpdater(std::make_unique(mFreqRuler->ruler, nullptr)); mFreqRuler->ruler.SetRange(0, mHiFreq); } @@ -2320,7 +2322,7 @@ void EffectEqualization::UpdateGraphic() EnvLinToLog(); mEnvelope = mLogEnvelope.get(); - mFreqRuler->ruler.SetLog(true); + mFreqRuler->ruler.SetUpdater(std::make_unique(mFreqRuler->ruler, nullptr)); mFreqRuler->ruler.SetRange(mLoFreq, mHiFreq); } @@ -2909,7 +2911,7 @@ void EffectEqualization::OnLinFreq(wxCommandEvent & WXUNUSED(event)) mLin = mLinFreq->IsChecked(); if(IsLinear()) //going from log to lin freq scale { - mFreqRuler->ruler.SetLog(false); + mFreqRuler->ruler.SetUpdater(std::make_unique(mFreqRuler->ruler, nullptr)); mFreqRuler->ruler.SetRange(0, mHiFreq); EnvLogToLin(); mEnvelope = mLinEnvelope.get(); @@ -2917,7 +2919,7 @@ void EffectEqualization::OnLinFreq(wxCommandEvent & WXUNUSED(event)) } else //going from lin to log freq scale { - mFreqRuler->ruler.SetLog(true); + mFreqRuler->ruler.SetUpdater(std::make_unique(mFreqRuler->ruler, nullptr)); mFreqRuler->ruler.SetRange(mLoFreq, mHiFreq); EnvLinToLog(); mEnvelope = mLogEnvelope.get(); diff --git a/src/tracks/playabletrack/wavetrack/ui/SpectrumVRulerControls.cpp b/src/tracks/playabletrack/wavetrack/ui/SpectrumVRulerControls.cpp index 9d203217a0f4..fe533718fe52 100644 --- a/src/tracks/playabletrack/wavetrack/ui/SpectrumVRulerControls.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/SpectrumVRulerControls.cpp @@ -20,6 +20,8 @@ Paul Licameli split from WaveTrackVRulerControls.cpp #include "../../../../WaveTrack.h" #include "../../../../prefs/SpectrogramSettings.h" #include "../../../../widgets/Ruler.h" +#include "../../../../widgets/LinearUpdater.h" +#include "../../../../widgets/LogarithmicUpdater.h" SpectrumVRulerControls::~SpectrumVRulerControls() = default; @@ -167,7 +169,7 @@ void SpectrumVRulerControls::DoUpdateVRuler( vruler->SetRange((int)(maxFreq), (int)(minFreq)); vruler->SetUnits({}); } - vruler->SetLog(false); + vruler->SetUpdater(std::make_unique(*vruler, nullptr)); } break; case SpectrogramSettings::stLogarithmic: @@ -189,7 +191,7 @@ void SpectrumVRulerControls::DoUpdateVRuler( vruler->SetLabelEdges(true); vruler->SetRange(maxFreq, minFreq); vruler->SetUnits({}); - vruler->SetLog(true); + vruler->SetUpdater(std::make_unique(*vruler, nullptr)); NumberScale scale( wt->GetSpectrogramSettings().GetScale( minFreq, maxFreq ) .Reversal() ); diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveformVRulerControls.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveformVRulerControls.cpp index e0b8691c3cb6..af2f7acd585f 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveformVRulerControls.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveformVRulerControls.cpp @@ -21,6 +21,7 @@ Paul Licameli split from WaveTrackVRulerControls.cpp #include "../../../../WaveTrack.h" #include "../../../../prefs/WaveformSettings.h" #include "../../../../widgets/Ruler.h" +#include "../../../../widgets/LinearUpdater.h" WaveformVRulerControls::~WaveformVRulerControls() = default; @@ -222,7 +223,7 @@ void WaveformVRulerControls::DoUpdateVRuler( vruler->SetFormat(Ruler::RealFormat); vruler->SetUnits({}); vruler->SetLabelEdges(false); - vruler->SetLog(false); + vruler->SetUpdater(std::make_unique(*vruler, nullptr)); } else { wxASSERT(scaleType == WaveformSettings::stLogarithmic); @@ -330,7 +331,7 @@ void WaveformVRulerControls::DoUpdateVRuler( #endif vruler->SetFormat(Ruler::RealLogFormat); vruler->SetLabelEdges(true); - vruler->SetLog(false); + vruler->SetUpdater(std::make_unique(*vruler, nullptr)); } vruler->GetMaxSize( &wt->vrulerSize.first, &wt->vrulerSize.second ); } diff --git a/src/tracks/timetrack/ui/TimeTrackVRulerControls.cpp b/src/tracks/timetrack/ui/TimeTrackVRulerControls.cpp index d7a3a0c88a0f..541500d1e2fd 100644 --- a/src/tracks/timetrack/ui/TimeTrackVRulerControls.cpp +++ b/src/tracks/timetrack/ui/TimeTrackVRulerControls.cpp @@ -25,6 +25,8 @@ Paul Licameli split from TrackPanel.cpp #include "../../../TrackPanelMouseEvent.h" #include "../../../UIHandle.h" #include "../../../widgets/Ruler.h" +#include "../../../widgets/LinearUpdater.h" +#include "../../../widgets/LogarithmicUpdater.h" TimeTrackVRulerControls::~TimeTrackVRulerControls() { @@ -120,7 +122,10 @@ void TimeTrackVRulerControls::UpdateRuler( const wxRect &rect ) vruler->SetFormat((tt->GetDisplayLog()) ? Ruler::RealLogFormat : Ruler::RealFormat); vruler->SetUnits({}); vruler->SetLabelEdges(false); - vruler->SetLog(tt->GetDisplayLog()); + if (tt->GetDisplayLog()) + vruler->SetUpdater(std::make_unique(*vruler, nullptr)); + else + vruler->SetUpdater(std::make_unique(*vruler, nullptr)); vruler->GetMaxSize( &tt->vrulerSize.first, &tt->vrulerSize.second ); } diff --git a/src/widgets/CustomUpdater.cpp b/src/widgets/CustomUpdater.cpp new file mode 100644 index 000000000000..f4b74520f264 --- /dev/null +++ b/src/widgets/CustomUpdater.cpp @@ -0,0 +1,32 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + CustomUpdater.cpp + + Dominic Mazzoni + +**********************************************************************/ + + +#include "CustomUpdater.h" + +void CustomUpdater::Update( + wxDC& dc, const Envelope* envelope, UpdateOutputs& allOutputs) const +{ + const int mLength = mRuler.mLength; + const Ruler::Fonts& mFonts = *mRuler.mpFonts; + + TickOutputs majorOutputs{ + allOutputs.majorLabels, allOutputs.bits, allOutputs.box }; + + // SET PARAMETER IN MCUSTOM CASE + // Works only with major labels + + int numLabel = allOutputs.majorLabels.size(); + + for (int i = 0; (i < numLabel) && (i <= mLength); ++i) + TickCustom(dc, i, mFonts.major, majorOutputs); + + BoxAdjust(allOutputs); +} diff --git a/src/widgets/CustomUpdater.h b/src/widgets/CustomUpdater.h new file mode 100644 index 000000000000..760f6f728e42 --- /dev/null +++ b/src/widgets/CustomUpdater.h @@ -0,0 +1,27 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + CustomUpdater.h + + Dominic Mazzoni + +**********************************************************************/ + +#ifndef __AUDACITY_CUSTOM_UPDATER__ +#define __AUDACITY_CUSTOM_UPDATER__ + +#include "Updater.h" + +struct CustomUpdater : public Updater { + explicit CustomUpdater(const Ruler& ruler, const ZoomInfo* z) + : Updater{ ruler, NULL } + {} + + void Update( + wxDC& dc, const Envelope* envelope, + UpdateOutputs& allOutputs + ) const override; +}; + +#endif diff --git a/src/widgets/LinearUpdater.cpp b/src/widgets/LinearUpdater.cpp new file mode 100644 index 000000000000..9251c08c0fb2 --- /dev/null +++ b/src/widgets/LinearUpdater.cpp @@ -0,0 +1,180 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + LinearUpdater.cpp + + Dominic Mazzoni + +**********************************************************************/ + +#include "LinearUpdater.h" + +void LinearUpdater::Update( + wxDC& dc, const Envelope* envelope, UpdateOutputs& allOutputs) const +{ + TickOutputs majorOutputs{ + allOutputs.majorLabels, allOutputs.bits, allOutputs.box }; + + const double mDbMirrorValue = mRuler.mDbMirrorValue; + const int mLength = mRuler.mLength; + const Ruler::RulerFormat mFormat = mRuler.mFormat; + + const int mLeft = mRuler.mLeft; + const int mTop = mRuler.mTop; + const int mBottom = mRuler.mBottom; + const int mRight = mRuler.mRight; + const int mOrientation = mRuler.mOrientation; + + const double mMin = mRuler.mMin; + const double mMax = mRuler.mMax; + const double mHiddenMin = mRuler.mHiddenMin; + const double mHiddenMax = mRuler.mHiddenMax; + + const Ruler::Fonts& mFonts = *mRuler.mpFonts; + const bool mLabelEdges = mRuler.mLabelEdges; + const int mLeftOffset = mRuler.mLeftOffset; + + // Use the "hidden" min and max to determine the tick size. + // That may make a difference with fisheye. + // Otherwise you may see the tick size for the whole ruler change + // when the fisheye approaches start or end. + double UPP = (mHiddenMax - mHiddenMin) / mLength; // Units per pixel + TickSizes tickSizes{ UPP, mOrientation, mFormat, false }; + + auto TickAtValue = + [this, &tickSizes, &dc, &majorOutputs, &mFonts, mOrientation, + mMin, mMax, mLength, mLeftOffset, mRight, mBottom] + (double value) -> int { + // Make a tick only if the value is strictly between the bounds + if (value <= std::min(mMin, mMax)) + return -1; + if (value >= std::max(mMin, mMax)) + return -1; + + int mid; + if (zoomInfo) { + // Tick only at zero + if (value) + return -1; + mid = (int)(zoomInfo->TimeToPosition(0.0, mLeftOffset)); + } + else + mid = (int)(mLength * ((mMin - value) / (mMin - mMax)) + 0.5); + + const int iMaxPos = (mOrientation == wxHORIZONTAL) ? mRight : mBottom - 5; + if (mid >= 0 && mid < iMaxPos) + Tick(dc, mid, value, tickSizes, mFonts.major, majorOutputs); + else + return -1; + + return mid; + }; + + if (mDbMirrorValue) { + // For dB scale, let the zeroes prevail over the extreme values if + // not the same, and let midline prevail over all + + // Do the midline + TickAtValue(-mDbMirrorValue); + + // Do the upper zero + TickAtValue(0.0); + + // Do the other zero + TickAtValue(-2 * mDbMirrorValue); + } + + // Extreme values + if (mLabelEdges) { + Tick(dc, 0, mMin, tickSizes, mFonts.major, majorOutputs); + Tick(dc, mLength, mMax, tickSizes, mFonts.major, majorOutputs); + } + + if (!mDbMirrorValue) { + // Zero (if it's strictly in the middle somewhere) + TickAtValue(0.0); + } + + double sign = UPP > 0.0 ? 1.0 : -1.0; + + int nDroppedMinorLabels = 0; + // Major and minor ticks + for (int jj = 0; jj < 2; ++jj) { + const double denom = jj == 0 ? tickSizes.mMajor : tickSizes.mMinor; + auto font = jj == 0 ? mFonts.major : mFonts.minor; + TickOutputs outputs{ + (jj == 0 ? allOutputs.majorLabels : allOutputs.minorLabels), + allOutputs.bits, allOutputs.box + }; + int ii = -1, j = 0; + double d, warpedD, nextD; + + double prevTime = 0.0, time = 0.0; + if (zoomInfo) { + j = zoomInfo->TimeToPosition(mMin); + prevTime = zoomInfo->PositionToTime(--j); + time = zoomInfo->PositionToTime(++j); + d = (prevTime + time) / 2.0; + } + else + d = mMin - UPP / 2; + if (envelope) + warpedD = ComputeWarpedLength(*envelope, 0.0, d); + else + warpedD = d; + // using ints doesn't work, as + // this will overflow and be negative at high zoom. + double step = floor(sign * warpedD / denom); + while (ii <= mLength) { + ii++; + if (zoomInfo) + { + prevTime = time; + time = zoomInfo->PositionToTime(++j); + nextD = (prevTime + time) / 2.0; + // wxASSERT(time >= prevTime); + } + else + nextD = d + UPP; + if (envelope) + warpedD += ComputeWarpedLength(*envelope, d, nextD); + else + warpedD = nextD; + d = nextD; + + if (floor(sign * warpedD / denom) > step) { + step = floor(sign * warpedD / denom); + bool major = jj == 0; + tickSizes.useMajor = major; + bool ticked = Tick(dc, ii, sign * step * denom, tickSizes, + font, outputs); + if (!major && !ticked) { + nDroppedMinorLabels++; + } + } + } + } + + tickSizes.useMajor = true; + + // If we've dropped minor labels through overcrowding, then don't show + // any of them. We're allowed though to drop ones which correspond to the + // major numbers. + if (nDroppedMinorLabels > + (allOutputs.majorLabels.size() + (mLabelEdges ? 2 : 0))) { + // Old code dropped the labels AND their ticks, like so: + // mMinorLabels.clear(); + // Nowadays we just drop the labels. + for (auto& label : allOutputs.minorLabels) + label.text = {}; + } + + // Left and Right Edges + if (mLabelEdges) { + Tick(dc, 0, mMin, tickSizes, mFonts.major, majorOutputs); + Tick(dc, mLength, mMax, tickSizes, mFonts.major, majorOutputs); + } + + BoxAdjust(allOutputs); +} diff --git a/src/widgets/LinearUpdater.h b/src/widgets/LinearUpdater.h new file mode 100644 index 000000000000..e35a49a6e7e5 --- /dev/null +++ b/src/widgets/LinearUpdater.h @@ -0,0 +1,27 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + LinearUpdater.h + + Dominic Mazzoni + +**********************************************************************/ + +#ifndef __AUDACITY_LINEAR_UPDATER__ +#define __AUDACITY_LINEAR_UPDATER__ + +#include "Updater.h" + +struct LinearUpdater : public Updater { + explicit LinearUpdater(const Ruler& ruler, const ZoomInfo* z) + : Updater{ ruler, z } + {} + + void Update( + wxDC& dc, const Envelope* envelope, + UpdateOutputs& allOutputs + ) const override; +}; + +#endif diff --git a/src/widgets/LogarithmicUpdater.cpp b/src/widgets/LogarithmicUpdater.cpp new file mode 100644 index 000000000000..8a3f43424b6c --- /dev/null +++ b/src/widgets/LogarithmicUpdater.cpp @@ -0,0 +1,121 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + LogarithmicUpdater.cpp + + Dominic Mazzoni + +**********************************************************************/ + +#include "LogarithmicUpdater.h" + +void LogarithmicUpdater::Update( + wxDC& dc, const Envelope* envelope, UpdateOutputs& allOutputs) const +{ + TickOutputs majorOutputs{ + allOutputs.majorLabels, allOutputs.bits, allOutputs.box }; + + const int mLength = mRuler.mLength; + const Ruler::RulerFormat mFormat = mRuler.mFormat; + + const int mOrientation = mRuler.mOrientation; + + const double mMin = mRuler.mMin; + const double mMax = mRuler.mMax; + const double mHiddenMin = mRuler.mHiddenMin; + const double mHiddenMax = mRuler.mHiddenMax; + + const Ruler::Fonts& mFonts = *mRuler.mpFonts; + const NumberScale mNumberScale = mRuler.mNumberScale; + + auto numberScale = (mNumberScale == NumberScale{}) + ? NumberScale(nstLogarithmic, mMin, mMax) + : mNumberScale; + + double UPP = (mHiddenMax - mHiddenMin) / mLength; // Units per pixel + TickSizes tickSizes{ UPP, mOrientation, mFormat, true }; + + tickSizes.mDigits = 2; //TODO: implement dynamic digit computation + + double loLog = log10(mMin); + double hiLog = log10(mMax); + int loDecade = (int)floor(loLog); + + double val; + double startDecade = pow(10., (double)loDecade); + + // Major ticks are the decades + double decade = startDecade; + double delta = hiLog - loLog, steps = fabs(delta); + double step = delta >= 0 ? 10 : 0.1; + double rMin = std::min(mMin, mMax), rMax = std::max(mMin, mMax); + for (int i = 0; i <= steps; i++) + { // if(i!=0) + { val = decade; + if (val >= rMin && val < rMax) { + const int pos(0.5 + mLength * numberScale.ValueToPosition(val)); + Tick(dc, pos, val, tickSizes, mFonts.major, majorOutputs); + } + } + decade *= step; + } + + // Minor ticks are multiples of decades + decade = startDecade; + float start, end, mstep; + if (delta > 0) + { + start = 2; end = 10; mstep = 1; + } + else + { + start = 9; end = 1; mstep = -1; + } + steps++; + tickSizes.useMajor = false; + TickOutputs minorOutputs{ + allOutputs.minorLabels, allOutputs.bits, allOutputs.box }; + for (int i = 0; i <= steps; i++) { + for (int j = start; j != end; j += mstep) { + val = decade * j; + if (val >= rMin && val < rMax) { + const int pos(0.5 + mLength * numberScale.ValueToPosition(val)); + Tick(dc, pos, val, tickSizes, mFonts.minor, minorOutputs); + } + } + decade *= step; + } + + // MinorMinor ticks are multiples of decades + decade = startDecade; + if (delta > 0) + { + start = 10; end = 100; mstep = 1; + } + else + { + start = 100; end = 10; mstep = -1; + } + steps++; + TickOutputs minorMinorOutputs{ + allOutputs.minorMinorLabels, allOutputs.bits, allOutputs.box }; + for (int i = 0; i <= steps; i++) { + // PRL: Bug1038. Don't label 1.6, rounded, as a duplicate tick for "2" + if (!(mFormat == Ruler::IntFormat && decade < 10.0)) { + for (int f = start; f != (int)(end); f += mstep) { + if ((int)(f / 10) != f / 10.0f) { + val = decade * f / 10; + if (val >= rMin && val < rMax) { + const int pos(0.5 + mLength * numberScale.ValueToPosition(val)); + Tick(dc, pos, val, tickSizes, + mFonts.minorMinor, minorMinorOutputs); + } + } + } + } + decade *= step; + } + + BoxAdjust(allOutputs); +} diff --git a/src/widgets/LogarithmicUpdater.h b/src/widgets/LogarithmicUpdater.h new file mode 100644 index 000000000000..bf02d0e92674 --- /dev/null +++ b/src/widgets/LogarithmicUpdater.h @@ -0,0 +1,28 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + LogarithmicUpdater.h + + Dominic Mazzoni + +**********************************************************************/ + +#ifndef __AUDACITY_LOGARITHMIC_UPDATER__ +#define __AUDACITY_LOGARITHMIC_UPDATER__ + +#include "Updater.h" + +struct LogarithmicUpdater : public Updater { + explicit LogarithmicUpdater(const Ruler& ruler, const ZoomInfo* z) + : Updater{ ruler, NULL } + {} + + void Update( + wxDC& dc, const Envelope* envelope, + UpdateOutputs& allOutputs + ) const override; +}; + +#endif + diff --git a/src/widgets/Ruler.cpp b/src/widgets/Ruler.cpp index df878074cbe0..9aa6314ca3ab 100644 --- a/src/widgets/Ruler.cpp +++ b/src/widgets/Ruler.cpp @@ -67,6 +67,12 @@ array of Ruler::Label. #include "Theme.h" #include "ViewInfo.h" +#include "Updater.h" +// Need to include to set default +#include "LinearUpdater.h" +// Needed for RulerPanel +#include "LogarithmicUpdater.h" + using std::min; using std::max; @@ -116,6 +122,12 @@ Ruler::Ruler() mTwoTone = false; mUseZoomInfo = NULL; + + // This part in particular needs inspection, not giving an error + // But is this corret? And should it be set to NULL or nullptr if a default + // cannot be made? + mpUpdater = std::make_unique( *this, mUseZoomInfo ); + // mpUpdater = nullptr; } Ruler::~Ruler() @@ -139,15 +151,29 @@ void Ruler::SetFormat(RulerFormat format) } } -void Ruler::SetLog(bool log) +void Ruler::SetUpdater(std::unique_ptr pUpdater) { - // Logarithmic + // Should a comparison be made between mpUpdater and pUpdater? + // Runtime type comparison isn't clean in c++ + mpUpdater = std::move(pUpdater); + Invalidate(); +} - if (mLog != log) { - mLog = log; +void Ruler::SetUpdater + (std::unique_ptr pUpdater, int leftOffset) +{ + // Should a comparison be made between mpUpdater and pUpdater? + // Runtime type comparison isn't clean in c++ + mpUpdater = std::move(pUpdater); - Invalidate(); - } + if (mLeftOffset != leftOffset) + mLeftOffset = leftOffset; + + // Hm, is this invalidation sufficient? What if *zoomInfo changes under us? + if (mUseZoomInfo != mpUpdater->zoomInfo) + mUseZoomInfo = mpUpdater->zoomInfo; + + Invalidate(); } void Ruler::SetUnits(const TranslatableString &units) @@ -354,407 +380,6 @@ void Ruler::Invalidate() // mUserBits.clear(); } -struct Ruler::TickSizes -{ - bool useMajor = true; - - double mMajor; - double mMinor; - - int mDigits; - -TickSizes( double UPP, int orientation, RulerFormat format, bool log ) -{ - //TODO: better dynamic digit computation for the log case - (void)log; - - // Given the dimensions of the ruler, the range of values it - // has to display, and the format (i.e. Int, Real, Time), - // figure out how many units are in one Minor tick, and - // in one Major tick. - // - // The goal is to always put tick marks on nice round numbers - // that are easy for humans to grok. This is the most tricky - // with time. - - double d; - - // As a heuristic, we want at least 22 pixels between each - // minor tick. We want to show numbers like "-48" - // in that space. - // If vertical, we don't need as much space. - double units = ((orientation == wxHORIZONTAL) ? 22 : 16) * fabs(UPP); - - mDigits = 0; - - switch(format) { - case LinearDBFormat: - if (units < 0.001) { - mMinor = 0.001; - mMajor = 0.005; - return; - } - if (units < 0.01) { - mMinor = 0.01; - mMajor = 0.05; - return; - } - if (units < 0.1) { - mMinor = 0.1; - mMajor = 0.5; - return; - } - if (units < 1.0) { - mMinor = 1.0; - mMajor = 6.0; - return; - } - if (units < 3.0) { - mMinor = 3.0; - mMajor = 12.0; - return; - } - if (units < 6.0) { - mMinor = 6.0; - mMajor = 24.0; - return; - } - if (units < 12.0) { - mMinor = 12.0; - mMajor = 48.0; - return; - } - if (units < 24.0) { - mMinor = 24.0; - mMajor = 96.0; - return; - } - d = 20.0; - for(;;) { - if (units < d) { - mMinor = d; - mMajor = d*5.0; - return; - } - d *= 5.0; - if (units < d) { - mMinor = d; - mMajor = d*5.0; - return; - } - d *= 2.0; - } - break; - - case IntFormat: - d = 1.0; - for(;;) { - if (units < d) { - mMinor = d; - mMajor = d*5.0; - return; - } - d *= 5.0; - if (units < d) { - mMinor = d; - mMajor = d*2.0; - return; - } - d *= 2.0; - } - break; - - case TimeFormat: - if (units > 0.5) { - if (units < 1.0) { // 1 sec - mMinor = 1.0; - mMajor = 5.0; - return; - } - if (units < 5.0) { // 5 sec - mMinor = 5.0; - mMajor = 15.0; - return; - } - if (units < 10.0) { - mMinor = 10.0; - mMajor = 30.0; - return; - } - if (units < 15.0) { - mMinor = 15.0; - mMajor = 60.0; - return; - } - if (units < 30.0) { - mMinor = 30.0; - mMajor = 60.0; - return; - } - if (units < 60.0) { // 1 min - mMinor = 60.0; - mMajor = 300.0; - return; - } - if (units < 300.0) { // 5 min - mMinor = 300.0; - mMajor = 900.0; - return; - } - if (units < 600.0) { // 10 min - mMinor = 600.0; - mMajor = 1800.0; - return; - } - if (units < 900.0) { // 15 min - mMinor = 900.0; - mMajor = 3600.0; - return; - } - if (units < 1800.0) { // 30 min - mMinor = 1800.0; - mMajor = 3600.0; - return; - } - if (units < 3600.0) { // 1 hr - mMinor = 3600.0; - mMajor = 6*3600.0; - return; - } - if (units < 6*3600.0) { // 6 hrs - mMinor = 6*3600.0; - mMajor = 24*3600.0; - return; - } - if (units < 24*3600.0) { // 1 day - mMinor = 24*3600.0; - mMajor = 7*24*3600.0; - return; - } - - mMinor = 24.0 * 7.0 * 3600.0; // 1 week - mMajor = 24.0 * 7.0 * 3600.0; - } - - // Otherwise fall through to RealFormat - // (fractions of a second should be dealt with - // the same way as for RealFormat) - - case RealFormat: - d = 0.000001; - // mDigits is number of digits after the decimal point. - mDigits = 6; - for(;;) { - if (units < d) { - mMinor = d; - mMajor = d*5.0; - return; - } - d *= 5.0; - if (units < d) { - mMinor = d; - mMajor = d*2.0; - return; - } - d *= 2.0; - mDigits--; - // More than 10 digit numbers? Something is badly wrong. - // Probably units is coming in with too high a value. - wxASSERT( mDigits >= -10 ); - if( mDigits < -10 ) - break; - } - mMinor = d; - mMajor = d * 2.0; - break; - - case RealLogFormat: - d = 0.000001; - // mDigits is number of digits after the decimal point. - mDigits = 6; - for(;;) { - if (units < d) { - mMinor = d; - mMajor = d*5.0; - return; - } - d *= 5.0; - if (units < d) { - mMinor = d; - mMajor = d*2.0; - return; - } - d *= 2.0; - mDigits--; - // More than 10 digit numbers? Something is badly wrong. - // Probably units is coming in with too high a value. - wxASSERT( mDigits >= -10 ); - if( mDigits < -10 ) - break; - } - mDigits++; - mMinor = d; - mMajor = d * 2.0; - break; - } -} - -TranslatableString LabelString( - double d, RulerFormat format, const TranslatableString &units ) - const -{ - // Given a value, turn it into a string according - // to the current ruler format. The number of digits of - // accuracy depends on the resolution of the ruler, - // i.e. how far zoomed in or out you are. - - wxString s; - - // PRL Todo: are all these cases properly localized? (Decimal points, - // hour-minute-second, etc.?) - - // Replace -0 with 0 - if (d < 0.0 && (d+mMinor > 0.0) && ( format != RealLogFormat )) - d = 0.0; - - switch( format ) { - case IntFormat: - s.Printf(wxT("%d"), (int)floor(d+0.5)); - break; - case LinearDBFormat: - if (mMinor >= 1.0) - s.Printf(wxT("%d"), (int)floor(d+0.5)); - else { - int precision = -log10(mMinor); - s.Printf(wxT("%.*f"), precision, d); - } - break; - case RealFormat: - if (mMinor >= 1.0) - s.Printf(wxT("%d"), (int)floor(d+0.5)); - else { - s.Printf(wxString::Format(wxT("%%.%df"), mDigits), d); - } - break; - case RealLogFormat: - if (mMinor >= 1.0) - s.Printf(wxT("%d"), (int)floor(d+0.5)); - else { - s.Printf(wxString::Format(wxT("%%.%df"), mDigits), d); - } - break; - case TimeFormat: - if (useMajor) { - if (d < 0) { - s = wxT("-"); - d = -d; - } - - #if ALWAYS_HH_MM_SS - int secs = (int)(d + 0.5); - if (mMinor >= 1.0) { - s.Printf(wxT("%d:%02d:%02d"), secs/3600, (secs/60)%60, secs%60); - } - else { - wxString t1, t2, format; - t1.Printf(wxT("%d:%02d:"), secs/3600, (secs/60)%60); - format.Printf(wxT("%%0%d.%dlf"), mDigits+3, mDigits); - t2.Printf(format, fmod(d, 60.0)); - s += t1 + t2; - } - break; - #endif - - if (mMinor >= 3600.0) { - int hrs = (int)(d / 3600.0 + 0.5); - wxString h; - h.Printf(wxT("%d:00:00"), hrs); - s += h; - } - else if (mMinor >= 60.0) { - int minutes = (int)(d / 60.0 + 0.5); - wxString m; - if (minutes >= 60) - m.Printf(wxT("%d:%02d:00"), minutes/60, minutes%60); - else - m.Printf(wxT("%d:00"), minutes); - s += m; - } - else if (mMinor >= 1.0) { - int secs = (int)(d + 0.5); - wxString t; - if (secs >= 3600) - t.Printf(wxT("%d:%02d:%02d"), secs/3600, (secs/60)%60, secs%60); - else if (secs >= 60) - t.Printf(wxT("%d:%02d"), secs/60, secs%60); - else - t.Printf(wxT("%d"), secs); - s += t; - } - else { -// Commented out old and incorrect code for avoiding the 40mins and 60 seconds problem -// It was causing Bug 463 - Incorrect Timeline numbering (where at high zoom and long tracks, -// numbers did not change. -#if 0 - // The casting to float is working around an issue where 59 seconds - // would show up as 60 when using g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3. - int secs = (int)(float)(d); - wxString t1, t2, format; - - if (secs >= 3600) - t1.Printf(wxT("%d:%02d:"), secs/3600, (secs/60)%60); - else if (secs >= 60) - t1.Printf(wxT("%d:"), secs/60); - - if (secs >= 60) - format.Printf(wxT("%%0%d.%dlf"), mDigits+3, mDigits); - else - format.Printf(wxT("%%%d.%dlf"), mDigits+3, mDigits); - // The casting to float is working around an issue where 59 seconds - // would show up as 60 when using g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3. - t2.Printf(format, fmod((float)d, (float)60.0)); -#else - // For d in the range of hours, d is just very slightly below the value it should - // have, because of using a double, which in turn yields values like 59:59:999999 - // mins:secs:nanosecs when we want 1:00:00:000000 - // so adjust by less than a nano second per hour to get nicer number formatting. - double dd = d * 1.000000000000001; - int secs = (int)(dd); - wxString t1, t2, format; - - if (secs >= 3600) - t1.Printf(wxT("%d:%02d:"), secs/3600, (secs/60)%60); - else if (secs >= 60) - t1.Printf(wxT("%d:"), secs/60); - - if (secs >= 60) - format.Printf(wxT("%%0%d.%dlf"), mDigits+3, mDigits); - else - format.Printf(wxT("%%%d.%dlf"), mDigits+3, mDigits); - // dd will be reduced to just the seconds and fractional part. - dd = dd - secs + (secs%60); - // truncate to appropriate number of digits, so that the print formatting - // doesn't round up 59.9999999 to 60. - double multiplier = pow( 10, mDigits); - dd = ((int)(dd * multiplier))/multiplier; - t2.Printf(format, dd); -#endif - s += t1 + t2; - } - } - else { - } - } - - auto result = Verbatim( s ); - if (!units.empty()) - result += units; - - return result; -} - -}; // struct Ruler::TickSizes - auto Ruler::MakeTick( Label lab, wxDC &dc, wxFont font, @@ -846,157 +471,12 @@ auto Ruler::MakeTick( return { { strLeft, strTop, strW, strH }, lab }; } -struct Ruler::Updater { - const Ruler &mRuler; - const ZoomInfo *zoomInfo; - - explicit Updater( const Ruler &ruler, const ZoomInfo *z ) - : mRuler{ ruler } - , zoomInfo{ z } - {} - - const double mDbMirrorValue = mRuler.mDbMirrorValue; - const int mLength = mRuler.mLength; - const RulerFormat mFormat = mRuler.mFormat; - const TranslatableString mUnits = mRuler.mUnits; - - const int mLeft = mRuler.mLeft; - const int mTop = mRuler.mTop; - const int mBottom = mRuler.mBottom; - const int mRight = mRuler.mRight; - - const int mSpacing = mRuler.mSpacing; - const int mOrientation = mRuler.mOrientation; - const bool mFlip = mRuler.mFlip; - - const bool mCustom = mRuler.mCustom; - const Fonts &mFonts = *mRuler.mpFonts; - const bool mLog = mRuler.mLog; - const double mHiddenMin = mRuler.mHiddenMin; - const double mHiddenMax = mRuler.mHiddenMax; - const bool mLabelEdges = mRuler.mLabelEdges; - const double mMin = mRuler.mMin; - const double mMax = mRuler.mMax; - const int mLeftOffset = mRuler.mLeftOffset; - const NumberScale mNumberScale = mRuler.mNumberScale; - - struct TickOutputs; - - bool Tick( wxDC &dc, - int pos, double d, const TickSizes &tickSizes, wxFont font, - TickOutputs outputs - ) const; - - // Another tick generator for custom ruler case (noauto) . - bool TickCustom( wxDC &dc, int labelIdx, wxFont font, - TickOutputs outputs - ) const; - - static void ChooseFonts( - std::unique_ptr &pFonts, const Fonts *pUserFonts, - wxDC &dc, int desiredPixelHeight ); - - struct UpdateOutputs; - - void Update( - wxDC &dc, const Envelope* envelope, - UpdateOutputs &allOutputs - )// Envelope *speedEnv, long minSpeed, long maxSpeed ) - const; - - void UpdateCustom( wxDC &dc, UpdateOutputs &allOutputs ) const; - void UpdateLinear( - wxDC &dc, const Envelope *envelope, UpdateOutputs &allOutputs ) const; - void UpdateNonlinear( wxDC &dc, UpdateOutputs &allOutputs ) const; -}; - struct Ruler::Cache { Bits mBits; Labels mMajorLabels, mMinorLabels, mMinorMinorLabels; wxRect mRect; }; -struct Ruler::Updater::TickOutputs{ Labels &labels; Bits &bits; wxRect &box; }; -struct Ruler::Updater::UpdateOutputs { - Labels &majorLabels, &minorLabels, &minorMinorLabels; - Bits &bits; - wxRect &box; -}; - -bool Ruler::Updater::Tick( wxDC &dc, - int pos, double d, const TickSizes &tickSizes, wxFont font, - // in/out: - TickOutputs outputs ) const -{ - // Bug 521. dB view for waveforms needs a 2-sided scale. - if(( mDbMirrorValue > 1.0 ) && ( -d > mDbMirrorValue )) - d = -2*mDbMirrorValue - d; - - // FIXME: We don't draw a tick if off end of our label arrays - // But we shouldn't have an array of labels. - if( outputs.labels.size() >= mLength ) - return false; - - Label lab; - lab.value = d; - lab.pos = pos; - lab.text = tickSizes.LabelString( d, mFormat, mUnits ); - - const auto result = MakeTick( - lab, - dc, font, - outputs.bits, - mLeft, mTop, mSpacing, mFonts.lead, - mFlip, - mOrientation ); - - auto &rect = result.first; - outputs.box.Union( rect ); - outputs.labels.emplace_back( result.second ); - return !rect.IsEmpty(); -} - -bool Ruler::Updater::TickCustom( wxDC &dc, int labelIdx, wxFont font, - // in/out: - TickOutputs outputs ) const -{ - // FIXME: We don't draw a tick if of end of our label arrays - // But we shouldn't have an array of labels. - if( labelIdx >= outputs.labels.size() ) - return false; - - //This should only used in the mCustom case - - Label lab; - lab.value = 0.0; - - const auto result = MakeTick( - lab, - - dc, font, - outputs.bits, - mLeft, mTop, mSpacing, mFonts.lead, - mFlip, - mOrientation ); - - auto &rect = result.first; - outputs.box.Union( rect ); - outputs.labels[labelIdx] = ( result.second ); - return !rect.IsEmpty(); -} - -namespace { -double ComputeWarpedLength(const Envelope &env, double t0, double t1) -{ - return env.IntegralOfInverse(t0, t1); -} - -double SolveWarpedLength(const Envelope &env, double t0, double length) -{ - return env.SolveIntegralOfInverse(t0, length); -} -} - static constexpr int MinPixelHeight = #ifdef __WXMSW__ 12; @@ -1014,20 +494,23 @@ static constexpr int MaxPixelHeight = #endif -void Ruler::Updater::ChooseFonts( - std::unique_ptr &pFonts, const Fonts *pUserFonts, - wxDC &dc, int desiredPixelHeight ) +void Ruler::ChooseFonts( wxDC &dc ) const { - if ( pFonts ) + const Fonts* pUserFonts = mpUserFonts.get(); + int desiredPixelHeight = mOrientation == wxHORIZONTAL + ? mBottom - mTop - 5 // height less ticks and 1px gap + : MaxPixelHeight; + + if (mpFonts) return; - if ( pUserFonts ) { - pFonts = std::make_unique( *pUserFonts ); + if (pUserFonts) { + mpFonts = std::make_unique(*pUserFonts); return; } - pFonts = std::make_unique( Fonts{ {}, {}, {}, 0 } ); - auto &fonts = *pFonts; + mpFonts = std::make_unique(Fonts{ {}, {}, {}, 0 }); + auto& fonts = *mpFonts; int fontSize = 4; @@ -1036,329 +519,18 @@ void Ruler::Updater::ChooseFonts( // Keep making the font bigger until it's too big, then subtract one. wxCoord height; - FindFontHeights( height, fonts.lead, dc, fontSize, wxFONTWEIGHT_BOLD ); + FindFontHeights(height, fonts.lead, dc, fontSize, wxFONTWEIGHT_BOLD); while (height <= desiredPixelHeight && fontSize < 40) { fontSize++; - FindFontHeights( height, fonts.lead, dc, fontSize, wxFONTWEIGHT_BOLD ); + FindFontHeights(height, fonts.lead, dc, fontSize, wxFONTWEIGHT_BOLD); } fontSize--; - FindFontHeights( height, fonts.lead, dc, fontSize ); + FindFontHeights(height, fonts.lead, dc, fontSize); fonts.major = wxFont{ fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD }; fonts.minor = wxFont{ fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL }; fonts.minorMinor = wxFont{ fontSize - 1, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL }; -} - -void Ruler::Updater::UpdateCustom( wxDC &dc, UpdateOutputs &allOutputs ) const -{ - TickOutputs majorOutputs{ - allOutputs.majorLabels, allOutputs.bits, allOutputs.box }; - - // SET PARAMETER IN MCUSTOM CASE - // Works only with major labels - - int numLabel = allOutputs.majorLabels.size(); - - for( int i = 0; (i int { - // Make a tick only if the value is strictly between the bounds - if ( value <= std::min( mMin, mMax ) ) - return -1; - if ( value >= std::max( mMin, mMax ) ) - return -1; - - int mid; - if (zoomInfo != NULL) { - // Tick only at zero - if ( value ) - return -1; - mid = (int)(zoomInfo->TimeToPosition(0.0, mLeftOffset)); - } - else - mid = (int)(mLength*((mMin - value) / (mMin - mMax)) + 0.5); - - const int iMaxPos = (mOrientation == wxHORIZONTAL) ? mRight : mBottom - 5; - if (mid >= 0 && mid < iMaxPos) - Tick( dc, mid, value, tickSizes, mFonts.major, majorOutputs ); - else - return -1; - - return mid; - }; - - if ( mDbMirrorValue ) { - // For dB scale, let the zeroes prevail over the extreme values if - // not the same, and let midline prevail over all - - // Do the midline - TickAtValue( -mDbMirrorValue ); - - // Do the upper zero - TickAtValue( 0.0 ); - - // Do the other zero - TickAtValue( -2 * mDbMirrorValue ); - } - - // Extreme values - if (mLabelEdges) { - Tick( dc, 0, mMin, tickSizes, mFonts.major, majorOutputs ); - Tick( dc, mLength, mMax, tickSizes, mFonts.major, majorOutputs ); - } - - if ( !mDbMirrorValue ) { - // Zero (if it's strictly in the middle somewhere) - TickAtValue( 0.0 ); - } - - double sg = UPP > 0.0? 1.0: -1.0; - - int nDroppedMinorLabels=0; - // Major and minor ticks - for (int jj = 0; jj < 2; ++jj) { - const double denom = jj == 0 ? tickSizes.mMajor : tickSizes.mMinor; - auto font = jj == 0 ? mFonts.major : mFonts.minor; - TickOutputs outputs{ - (jj == 0 ? allOutputs.majorLabels : allOutputs.minorLabels), - allOutputs.bits, allOutputs.box - }; - int ii = -1, j = 0; - double d, warpedD, nextD; - - double prevTime = 0.0, time = 0.0; - if (zoomInfo != NULL) { - j = zoomInfo->TimeToPosition(mMin); - prevTime = zoomInfo->PositionToTime(--j); - time = zoomInfo->PositionToTime(++j); - d = (prevTime + time) / 2.0; - } - else - d = mMin - UPP / 2; - if (envelope) - warpedD = ComputeWarpedLength(*envelope, 0.0, d); - else - warpedD = d; - // using ints doesn't work, as - // this will overflow and be negative at high zoom. - double step = floor(sg * warpedD / denom); - while (ii <= mLength) { - ii++; - if (zoomInfo) - { - prevTime = time; - time = zoomInfo->PositionToTime(++j); - nextD = (prevTime + time) / 2.0; - // wxASSERT(time >= prevTime); - } - else - nextD = d + UPP; - if (envelope) - warpedD += ComputeWarpedLength(*envelope, d, nextD); - else - warpedD = nextD; - d = nextD; - - if (floor(sg * warpedD / denom) > step) { - step = floor(sg * warpedD / denom); - bool major = jj == 0; - tickSizes.useMajor = major; - bool ticked = Tick( dc, ii, sg * step * denom, tickSizes, - font, outputs ); - if( !major && !ticked ){ - nDroppedMinorLabels++; - } - } - } - } - - tickSizes.useMajor = true; - - // If we've dropped minor labels through overcrowding, then don't show - // any of them. We're allowed though to drop ones which correspond to the - // major numbers. - if( nDroppedMinorLabels > - (allOutputs.majorLabels.size() + (mLabelEdges ? 2:0)) ){ - // Old code dropped the labels AND their ticks, like so: - // mMinorLabels.clear(); - // Nowadays we just drop the labels. - for( auto &label : allOutputs.minorLabels ) - label.text = {}; - } - - // Left and Right Edges - if (mLabelEdges) { - Tick( dc, 0, mMin, tickSizes, mFonts.major, majorOutputs ); - Tick( dc, mLength, mMax, tickSizes, mFonts.major, majorOutputs ); - } -} - -void Ruler::Updater::UpdateNonlinear( - wxDC &dc, UpdateOutputs &allOutputs ) const -{ - TickOutputs majorOutputs{ - allOutputs.majorLabels, allOutputs.bits, allOutputs.box }; - - auto numberScale = ( mNumberScale == NumberScale{} ) - ? NumberScale( nstLogarithmic, mMin, mMax ) - : mNumberScale; - - double UPP = (mHiddenMax-mHiddenMin)/mLength; // Units per pixel - TickSizes tickSizes{ UPP, mOrientation, mFormat, true }; - - tickSizes.mDigits = 2; //TODO: implement dynamic digit computation - - double loLog = log10(mMin); - double hiLog = log10(mMax); - int loDecade = (int) floor(loLog); - - double val; - double startDecade = pow(10., (double)loDecade); - - // Major ticks are the decades - double decade = startDecade; - double delta=hiLog-loLog, steps=fabs(delta); - double step = delta>=0 ? 10 : 0.1; - double rMin=std::min(mMin, mMax), rMax=std::max(mMin, mMax); - for(int i=0; i<=steps; i++) - { // if(i!=0) - { val = decade; - if(val >= rMin && val < rMax) { - const int pos(0.5 + mLength * numberScale.ValueToPosition(val)); - Tick( dc, pos, val, tickSizes, mFonts.major, majorOutputs ); - } - } - decade *= step; - } - - // Minor ticks are multiples of decades - decade = startDecade; - float start, end, mstep; - if (delta > 0) - { start=2; end=10; mstep=1; - }else - { start=9; end=1; mstep=-1; - } - steps++; - tickSizes.useMajor = false; - TickOutputs minorOutputs{ - allOutputs.minorLabels, allOutputs.bits, allOutputs.box }; - for(int i=0; i<=steps; i++) { - for(int j=start; j!=end; j+=mstep) { - val = decade * j; - if(val >= rMin && val < rMax) { - const int pos(0.5 + mLength * numberScale.ValueToPosition(val)); - Tick( dc, pos, val, tickSizes, mFonts.minor, minorOutputs ); - } - } - decade *= step; - } - - // MinorMinor ticks are multiples of decades - decade = startDecade; - if (delta > 0) - { start= 10; end=100; mstep= 1; - }else - { start=100; end= 10; mstep=-1; - } - steps++; - TickOutputs minorMinorOutputs{ - allOutputs.minorMinorLabels, allOutputs.bits, allOutputs.box }; - for (int i = 0; i <= steps; i++) { - // PRL: Bug1038. Don't label 1.6, rounded, as a duplicate tick for "2" - if (!(mFormat == IntFormat && decade < 10.0)) { - for (int f = start; f != (int)(end); f += mstep) { - if ((int)(f / 10) != f / 10.0f) { - val = decade * f / 10; - if (val >= rMin && val < rMax) { - const int pos(0.5 + mLength * numberScale.ValueToPosition(val)); - Tick( dc, pos, val, tickSizes, - mFonts.minorMinor, minorMinorOutputs ); - } - } - } - } - decade *= step; - } -} - -void Ruler::Updater::Update( - wxDC &dc, const Envelope* envelope, - UpdateOutputs &allOutputs -)// Envelope *speedEnv, long minSpeed, long maxSpeed ) - const -{ - TickOutputs majorOutputs{ - allOutputs.majorLabels, allOutputs.bits, allOutputs.box }; - - if ( mCustom ) - UpdateCustom( dc, allOutputs ); - else if ( !mLog ) - UpdateLinear( dc, envelope, allOutputs ); - else - UpdateNonlinear( dc, allOutputs ); - - int displacementx=0, displacementy=0; - auto &box = allOutputs.box; - if (!mFlip) { - if (mOrientation==wxHORIZONTAL) { - int d = mTop + box.GetHeight() + 5; - box.Offset(0,d); - box.Inflate(0,5); - displacementx=0; - displacementy=d; - } - else { - int d = mLeft - box.GetLeft() + 5; - box.Offset(d,0); - box.Inflate(5,0); - displacementx=d; - displacementy=0; - } - } - else { - if (mOrientation==wxHORIZONTAL) { - box.Inflate(0,5); - displacementx=0; - displacementy=0; - } - } - auto update = [=]( Label &label ){ - label.lx += displacementx; - label.ly += displacementy; - }; - for( auto &label : allOutputs.majorLabels ) - update( label ); - for( auto &label : allOutputs.minorLabels ) - update( label ); - for( auto &label : allOutputs.minorMinorLabels ) - update( label ); -} - -void Ruler::ChooseFonts( wxDC &dc ) const -{ - Updater::ChooseFonts( mpFonts, mpUserFonts.get(), dc, - mOrientation == wxHORIZONTAL - ? mBottom - mTop - 5 // height less ticks and 1px gap - : MaxPixelHeight - ); + } void Ruler::UpdateCache( @@ -1368,10 +540,6 @@ void Ruler::UpdateCache( if ( mpCache ) return; - const ZoomInfo *zoomInfo = NULL; - if (!mLog && mOrientation == wxHORIZONTAL) - zoomInfo = mUseZoomInfo; - // This gets called when something has been changed // (i.e. we've been invalidated). Recompute all // tick positions and font size. @@ -1404,15 +572,12 @@ void Ruler::UpdateCache( cache.mBits = mUserBits; cache.mBits.resize( static_cast(mLength + 1), false ); - - // Keep Updater const! We want no hidden state changes affecting its - // computations. - const Updater updater{ *this, zoomInfo }; + Updater::UpdateOutputs allOutputs{ cache.mMajorLabels, cache.mMinorLabels, cache.mMinorMinorLabels, cache.mBits, cache.mRect }; - updater.Update(dc, envelope, allOutputs); + mpUpdater->Update(dc, envelope, allOutputs); } auto Ruler::GetFonts() const -> Fonts @@ -1677,19 +842,6 @@ void Ruler::Label::Draw(wxDC&dc, bool twoTone, wxColour c) const } } -void Ruler::SetUseZoomInfo(int leftOffset, const ZoomInfo *zoomInfo) -{ - - if ( mLeftOffset != leftOffset || - // Hm, is this invalidation sufficient? What if *zoomInfo changes under us? - mUseZoomInfo != zoomInfo - ) { - mLeftOffset = leftOffset; - mUseZoomInfo = zoomInfo; - Invalidate(); - } -} - // // RulerPanel // @@ -1716,7 +868,10 @@ RulerPanel::RulerPanel(wxWindow* parent, wxWindowID id, ruler.SetBounds( 0, 0, bounds.x, bounds.y ); ruler.SetOrientation(orientation); ruler.SetRange( range.first, range.second ); - ruler.SetLog( options.log ); + if (options.log) + ruler.SetUpdater(std::make_unique(ruler, nullptr)); + else + ruler.SetUpdater(std::make_unique(ruler, nullptr)); ruler.SetFormat(format); ruler.SetUnits( units ); ruler.SetFlip( options.flip ); diff --git a/src/widgets/Ruler.h b/src/widgets/Ruler.h index 41b0155e0852..dcede69b6336 100644 --- a/src/widgets/Ruler.h +++ b/src/widgets/Ruler.h @@ -22,6 +22,7 @@ class wxFont; class Envelope; class ZoomInfo; +struct Updater; class AUDACITY_DLL_API Ruler { public: @@ -62,6 +63,13 @@ class AUDACITY_DLL_API Ruler { // (at the center of the pixel, in both cases) void SetRange(double min, double max, double hiddenMin, double hiddenMax); + // Set the kind of updater the ruler will use + // (Linear, Logarithmic, Custom, etc.) + void SetUpdater(std::unique_ptr pUpdater); + + // An overload to replace SetUseZoomInfo + void SetUpdater(std::unique_ptr pUpdater, int leftOffset); + // // Optional Ruler Parameters // @@ -77,9 +85,6 @@ class AUDACITY_DLL_API Ruler { void SetUnits(const TranslatableString &units); void SetDbMirrorValue( const double d ); - // Logarithmic - void SetLog(bool log); - // Minimum number of pixels between labels void SetSpacing(int spacing); @@ -130,8 +135,6 @@ class AUDACITY_DLL_API Ruler { void SetCustomMinorLabels( const TranslatableStrings &labels, int start, int step); - void SetUseZoomInfo(int leftOffset, const ZoomInfo *zoomInfo); - // // Drawing // @@ -152,7 +155,10 @@ class AUDACITY_DLL_API Ruler { void Invalidate(); private: - struct TickSizes; + friend class Updater; + friend class LinearUpdater; + friend class LogarithmicUpdater; + friend class CustomUpdater; class Label { public: @@ -170,8 +176,6 @@ class AUDACITY_DLL_API Ruler { void ChooseFonts( wxDC &dc ) const; void UpdateCache( wxDC &dc, const Envelope* envelope ) const; - - struct Updater; public: bool mbTicksOnly; // true => no line the length of the ruler @@ -187,6 +191,8 @@ class AUDACITY_DLL_API Ruler { std::unique_ptr mpUserFonts; mutable std::unique_ptr mpFonts; + std::unique_ptr mpUpdater; + double mMin, mMax; double mHiddenMin, mHiddenMax; diff --git a/src/widgets/Updater.cpp b/src/widgets/Updater.cpp new file mode 100644 index 000000000000..df5d04a06df9 --- /dev/null +++ b/src/widgets/Updater.cpp @@ -0,0 +1,552 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + Updater.cpp + + Dominic Mazzoni + +*******************************************************************//** + +\class Updater +\brief Used to update a Ruler. + + This is a pure virtual class which sets how a ruler will generate + its values. + + It supports three subclasses: LinearUpdater, LogarithmicUpdater, + and CustomUpdater + +*//******************************************************************/ + +#include "Updater.h" + +Updater::TickSizes::TickSizes(double UPP, int orientation, Ruler::RulerFormat format, bool log) + { + //TODO: better dynamic digit computation for the log case + (void)log; + + // Given the dimensions of the ruler, the range of values it + // has to display, and the format (i.e. Int, Real, Time), + // figure out how many units are in one Minor tick, and + // in one Major tick. + // + // The goal is to always put tick marks on nice round numbers + // that are easy for humans to grok. This is the most tricky + // with time. + + double d; + + // As a heuristic, we want at least 22 pixels between each + // minor tick. We want to show numbers like "-48" + // in that space. + // If vertical, we don't need as much space. + double units = ((orientation == wxHORIZONTAL) ? 22 : 16) * fabs(UPP); + + mDigits = 0; + + switch (format) { + case Ruler::LinearDBFormat: + if (units < 0.001) { + mMinor = 0.001; + mMajor = 0.005; + return; + } + if (units < 0.01) { + mMinor = 0.01; + mMajor = 0.05; + return; + } + if (units < 0.1) { + mMinor = 0.1; + mMajor = 0.5; + return; + } + if (units < 1.0) { + mMinor = 1.0; + mMajor = 6.0; + return; + } + if (units < 3.0) { + mMinor = 3.0; + mMajor = 12.0; + return; + } + if (units < 6.0) { + mMinor = 6.0; + mMajor = 24.0; + return; + } + if (units < 12.0) { + mMinor = 12.0; + mMajor = 48.0; + return; + } + if (units < 24.0) { + mMinor = 24.0; + mMajor = 96.0; + return; + } + d = 20.0; + for (;;) { + if (units < d) { + mMinor = d; + mMajor = d * 5.0; + return; + } + d *= 5.0; + if (units < d) { + mMinor = d; + mMajor = d * 5.0; + return; + } + d *= 2.0; + } + break; + + case Ruler::IntFormat: + d = 1.0; + for (;;) { + if (units < d) { + mMinor = d; + mMajor = d * 5.0; + return; + } + d *= 5.0; + if (units < d) { + mMinor = d; + mMajor = d * 2.0; + return; + } + d *= 2.0; + } + break; + + case Ruler::TimeFormat: + if (units > 0.5) { + if (units < 1.0) { // 1 sec + mMinor = 1.0; + mMajor = 5.0; + return; + } + if (units < 5.0) { // 5 sec + mMinor = 5.0; + mMajor = 15.0; + return; + } + if (units < 10.0) { + mMinor = 10.0; + mMajor = 30.0; + return; + } + if (units < 15.0) { + mMinor = 15.0; + mMajor = 60.0; + return; + } + if (units < 30.0) { + mMinor = 30.0; + mMajor = 60.0; + return; + } + if (units < 60.0) { // 1 min + mMinor = 60.0; + mMajor = 300.0; + return; + } + if (units < 300.0) { // 5 min + mMinor = 300.0; + mMajor = 900.0; + return; + } + if (units < 600.0) { // 10 min + mMinor = 600.0; + mMajor = 1800.0; + return; + } + if (units < 900.0) { // 15 min + mMinor = 900.0; + mMajor = 3600.0; + return; + } + if (units < 1800.0) { // 30 min + mMinor = 1800.0; + mMajor = 3600.0; + return; + } + if (units < 3600.0) { // 1 hr + mMinor = 3600.0; + mMajor = 6 * 3600.0; + return; + } + if (units < 6 * 3600.0) { // 6 hrs + mMinor = 6 * 3600.0; + mMajor = 24 * 3600.0; + return; + } + if (units < 24 * 3600.0) { // 1 day + mMinor = 24 * 3600.0; + mMajor = 7 * 24 * 3600.0; + return; + } + + mMinor = 24.0 * 7.0 * 3600.0; // 1 week + mMajor = 24.0 * 7.0 * 3600.0; + } + + // Otherwise fall through to RealFormat + // (fractions of a second should be dealt with + // the same way as for RealFormat) + + case Ruler::RealFormat: + d = 0.000001; + // mDigits is number of digits after the decimal point. + mDigits = 6; + for (;;) { + if (units < d) { + mMinor = d; + mMajor = d * 5.0; + return; + } + d *= 5.0; + if (units < d) { + mMinor = d; + mMajor = d * 2.0; + return; + } + d *= 2.0; + mDigits--; + // More than 10 digit numbers? Something is badly wrong. + // Probably units is coming in with too high a value. + wxASSERT(mDigits >= -10); + if (mDigits < -10) + break; + } + mMinor = d; + mMajor = d * 2.0; + break; + + case Ruler::RealLogFormat: + d = 0.000001; + // mDigits is number of digits after the decimal point. + mDigits = 6; + for (;;) { + if (units < d) { + mMinor = d; + mMajor = d * 5.0; + return; + } + d *= 5.0; + if (units < d) { + mMinor = d; + mMajor = d * 2.0; + return; + } + d *= 2.0; + mDigits--; + // More than 10 digit numbers? Something is badly wrong. + // Probably units is coming in with too high a value. + wxASSERT(mDigits >= -10); + if (mDigits < -10) + break; + } + mDigits++; + mMinor = d; + mMajor = d * 2.0; + break; + } + } + +TranslatableString Updater::TickSizes::LabelString( + double d, Ruler::RulerFormat format, const TranslatableString& units) + const + { + // Given a value, turn it into a string according + // to the current ruler format. The number of digits of + // accuracy depends on the resolution of the ruler, + // i.e. how far zoomed in or out you are. + + wxString s; + + // PRL Todo: are all these cases properly localized? (Decimal points, + // hour-minute-second, etc.?) + + // Replace -0 with 0 + if (d < 0.0 && (d + mMinor > 0.0) && (format != Ruler::RealLogFormat)) + d = 0.0; + + switch (format) { + case Ruler::IntFormat: + s.Printf(wxT("%d"), (int)floor(d + 0.5)); + break; + case Ruler::LinearDBFormat: + if (mMinor >= 1.0) + s.Printf(wxT("%d"), (int)floor(d + 0.5)); + else { + int precision = -log10(mMinor); + s.Printf(wxT("%.*f"), precision, d); + } + break; + case Ruler::RealFormat: + if (mMinor >= 1.0) + s.Printf(wxT("%d"), (int)floor(d + 0.5)); + else { + s.Printf(wxString::Format(wxT("%%.%df"), mDigits), d); + } + break; + case Ruler::RealLogFormat: + if (mMinor >= 1.0) + s.Printf(wxT("%d"), (int)floor(d + 0.5)); + else { + s.Printf(wxString::Format(wxT("%%.%df"), mDigits), d); + } + break; + case Ruler::TimeFormat: + if (useMajor) { + if (d < 0) { + s = wxT("-"); + d = -d; + } + +#if ALWAYS_HH_MM_SS + int secs = (int)(d + 0.5); + if (mMinor >= 1.0) { + s.Printf(wxT("%d:%02d:%02d"), secs / 3600, (secs / 60) % 60, secs % 60); + } + else { + wxString t1, t2, format; + t1.Printf(wxT("%d:%02d:"), secs / 3600, (secs / 60) % 60); + format.Printf(wxT("%%0%d.%dlf"), mDigits + 3, mDigits); + t2.Printf(format, fmod(d, 60.0)); + s += t1 + t2; + } + break; +#endif + + if (mMinor >= 3600.0) { + int hrs = (int)(d / 3600.0 + 0.5); + wxString h; + h.Printf(wxT("%d:00:00"), hrs); + s += h; + } + else if (mMinor >= 60.0) { + int minutes = (int)(d / 60.0 + 0.5); + wxString m; + if (minutes >= 60) + m.Printf(wxT("%d:%02d:00"), minutes / 60, minutes % 60); + else + m.Printf(wxT("%d:00"), minutes); + s += m; + } + else if (mMinor >= 1.0) { + int secs = (int)(d + 0.5); + wxString t; + if (secs >= 3600) + t.Printf(wxT("%d:%02d:%02d"), secs / 3600, (secs / 60) % 60, secs % 60); + else if (secs >= 60) + t.Printf(wxT("%d:%02d"), secs / 60, secs % 60); + else + t.Printf(wxT("%d"), secs); + s += t; + } + else { + // Commented out old and incorrect code for avoiding the 40mins and 60 seconds problem + // It was causing Bug 463 - Incorrect Timeline numbering (where at high zoom and long tracks, + // numbers did not change. +#if 0 + // The casting to float is working around an issue where 59 seconds + // would show up as 60 when using g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3. + int secs = (int)(float)(d); + wxString t1, t2, format; + + if (secs >= 3600) + t1.Printf(wxT("%d:%02d:"), secs / 3600, (secs / 60) % 60); + else if (secs >= 60) + t1.Printf(wxT("%d:"), secs / 60); + + if (secs >= 60) + format.Printf(wxT("%%0%d.%dlf"), mDigits + 3, mDigits); + else + format.Printf(wxT("%%%d.%dlf"), mDigits + 3, mDigits); + // The casting to float is working around an issue where 59 seconds + // would show up as 60 when using g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3. + t2.Printf(format, fmod((float)d, (float)60.0)); +#else + // For d in the range of hours, d is just very slightly below the value it should + // have, because of using a double, which in turn yields values like 59:59:999999 + // mins:secs:nanosecs when we want 1:00:00:000000 + // so adjust by less than a nano second per hour to get nicer number formatting. + double dd = d * 1.000000000000001; + int secs = (int)(dd); + wxString t1, t2, format; + + if (secs >= 3600) + t1.Printf(wxT("%d:%02d:"), secs / 3600, (secs / 60) % 60); + else if (secs >= 60) + t1.Printf(wxT("%d:"), secs / 60); + + if (secs >= 60) + format.Printf(wxT("%%0%d.%dlf"), mDigits + 3, mDigits); + else + format.Printf(wxT("%%%d.%dlf"), mDigits + 3, mDigits); + // dd will be reduced to just the seconds and fractional part. + dd = dd - secs + (secs % 60); + // truncate to appropriate number of digits, so that the print formatting + // doesn't round up 59.9999999 to 60. + double multiplier = pow(10, mDigits); + dd = ((int)(dd * multiplier)) / multiplier; + t2.Printf(format, dd); +#endif + s += t1 + t2; + } + } + else { + } + } + + auto result = Verbatim(s); + if (!units.empty()) + result += units; + + return result; + } + +void Updater::BoxAdjust( + UpdateOutputs& allOutputs +) +const +{ + const int mLeft = mRuler.mLeft; + const int mTop = mRuler.mTop; + const int mBottom = mRuler.mBottom; + const int mRight = mRuler.mRight; + const int mOrientation = mRuler.mOrientation; + const bool mFlip = mRuler.mFlip; + + int displacementx = 0, displacementy = 0; + auto& box = allOutputs.box; + if (!mFlip) { + if (mOrientation == wxHORIZONTAL) { + int d = mTop + box.GetHeight() + 5; + box.Offset(0, d); + box.Inflate(0, 5); + displacementx = 0; + displacementy = d; + } + else { + int d = mLeft - box.GetLeft() + 5; + box.Offset(d, 0); + box.Inflate(5, 0); + displacementx = d; + displacementy = 0; + } + } + else { + if (mOrientation == wxHORIZONTAL) { + box.Inflate(0, 5); + displacementx = 0; + displacementy = 0; + } + } + auto update = [=](Ruler::Label& label) { + label.lx += displacementx; + label.ly += displacementy; + }; + for (auto& label : allOutputs.majorLabels) + update(label); + for (auto& label : allOutputs.minorLabels) + update(label); + for (auto& label : allOutputs.minorMinorLabels) + update(label); +} + +bool Updater::Tick(wxDC& dc, + int pos, double d, const TickSizes& tickSizes, wxFont font, + // in/out: + TickOutputs outputs) const +{ + const double mDbMirrorValue = mRuler.mDbMirrorValue; + const int mLength = mRuler.mLength; + const Ruler::RulerFormat mFormat = mRuler.mFormat; + + const int mLeft = mRuler.mLeft; + const int mTop = mRuler.mTop; + const int mBottom = mRuler.mBottom; + const int mRight = mRuler.mRight; + const int mOrientation = mRuler.mOrientation; + + const Ruler::Fonts& mFonts = *mRuler.mpFonts; + const TranslatableString mUnits = mRuler.mUnits; + const int mSpacing = mRuler.mSpacing; + const bool mFlip = mRuler.mFlip; + + // Bug 521. dB view for waveforms needs a 2-sided scale. + if ((mDbMirrorValue > 1.0) && (-d > mDbMirrorValue)) + d = -2 * mDbMirrorValue - d; + + // FIXME: We don't draw a tick if off end of our label arrays + // But we shouldn't have an array of labels. + if (outputs.labels.size() >= mLength) + return false; + + Ruler::Label lab; + lab.value = d; + lab.pos = pos; + lab.text = tickSizes.LabelString(d, mFormat, mUnits); + + const auto result = Ruler::MakeTick( + lab, + dc, font, + outputs.bits, + mLeft, mTop, mSpacing, mFonts.lead, + mFlip, + mOrientation); + + auto& rect = result.first; + outputs.box.Union(rect); + outputs.labels.emplace_back(result.second); + return !rect.IsEmpty(); +} + +bool Updater::TickCustom(wxDC& dc, int labelIdx, wxFont font, + // in/out: + TickOutputs outputs) const +{ + const int mLeft = mRuler.mLeft; + const int mTop = mRuler.mTop; + const int mOrientation = mRuler.mOrientation; + + const Ruler::Fonts& mFonts = *mRuler.mpFonts; + const int mSpacing = mRuler.mSpacing; + const bool mFlip = mRuler.mFlip; + + // FIXME: We don't draw a tick if of end of our label arrays + // But we shouldn't have an array of labels. + if (labelIdx >= outputs.labels.size()) + return false; + + //This should only used in the mCustom case + + Ruler::Label lab; + lab.value = 0.0; + + const auto result = Ruler::MakeTick( + lab, + + dc, font, + outputs.bits, + mLeft, mTop, mSpacing, mFonts.lead, + mFlip, + mOrientation); + + auto& rect = result.first; + outputs.box.Union(rect); + outputs.labels[labelIdx] = (result.second); + return !rect.IsEmpty(); +} + +// Theoretically this helps +void Updater::Update( + wxDC& dc, const Envelope* envelope, + UpdateOutputs& allOutputs +) const {} diff --git a/src/widgets/Updater.h b/src/widgets/Updater.h new file mode 100644 index 000000000000..08e7fde053ed --- /dev/null +++ b/src/widgets/Updater.h @@ -0,0 +1,84 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + Updater.h + + Dominic Mazzoni + +**********************************************************************/ + +#ifndef __AUDACITY_UPDATER__ +#define __AUDACITY_UPDATER__ + +#include "Ruler.h" +#include "ViewInfo.h" // for children +#include "Envelope.h" + +struct Updater { + const Ruler& mRuler; + const ZoomInfo* zoomInfo; + + explicit Updater(const Ruler& ruler, const ZoomInfo* z) + : mRuler{ ruler } + , zoomInfo{ z } + {} + ~Updater() {} + + struct TickOutputs { Ruler::Labels& labels; Ruler::Bits& bits; wxRect& box; }; + struct UpdateOutputs { + Ruler::Labels& majorLabels, & minorLabels, & minorMinorLabels; + Ruler::Bits& bits; + wxRect& box; + }; + + + struct TickSizes + { + bool useMajor = true; + + double mMajor; + double mMinor; + + int mDigits; + + TickSizes(double UPP, int orientation, Ruler::RulerFormat format, bool log); + + TranslatableString LabelString( + double d, Ruler::RulerFormat format, const TranslatableString& units) + const; + }; + + double ComputeWarpedLength(const Envelope& env, double t0, double t1) const + { + return env.IntegralOfInverse(t0, t1); + } + + double SolveWarpedLength(const Envelope& env, double t0, double length) const + { + return env.SolveIntegralOfInverse(t0, length); + } + + bool Tick(wxDC& dc, + int pos, double d, const TickSizes& tickSizes, wxFont font, + TickOutputs outputs + ) const; + + // Another tick generator for custom ruler case (noauto) . + bool TickCustom(wxDC& dc, int labelIdx, wxFont font, + TickOutputs outputs + ) const; + + void BoxAdjust( + UpdateOutputs& allOutputs + ) + const; + + virtual void Update( + wxDC& dc, const Envelope* envelope, + UpdateOutputs& allOutputs + )// Envelope *speedEnv, long minSpeed, long maxSpeed ) + const = 0; +}; + +#endif //define __AUDACITY_UPDATER__ From d9619b98fc65e860ead5d60bbc90b20c7788fd0c Mon Sep 17 00:00:00 2001 From: micpap25 Date: Sun, 17 Jul 2022 15:12:48 -0400 Subject: [PATCH 02/17] Remove circular dependencies and create Virtual destructor Create the virtual destructor for updater Remove circular dependencies of Updater and Ruler Streamline the dependencies of Updater and Ruler. Extract all necessary data for Updater functions down to Updater, and rework ruler calls to use this format. This allows Updater to be independent of Ruler. --- src/AdornedRulerPanel.cpp | 6 +- src/FreqWindow.cpp | 18 +- src/Printing.cpp | 2 +- src/TimeTrack.cpp | 8 +- src/effects/Compressor.cpp | 4 +- src/effects/Equalization.cpp | 12 +- src/effects/ScienFilter.cpp | 4 +- .../wavetrack/ui/SpectrumVRulerControls.cpp | 8 +- .../wavetrack/ui/WaveformVRulerControls.cpp | 8 +- .../timetrack/ui/TimeTrackVRulerControls.cpp | 6 +- src/widgets/CustomUpdater.cpp | 13 +- src/widgets/CustomUpdater.h | 7 +- src/widgets/LinearUpdater.cpp | 51 +-- src/widgets/LinearUpdater.h | 7 +- src/widgets/LogarithmicUpdater.cpp | 33 +- src/widgets/LogarithmicUpdater.h | 7 +- src/widgets/MeterPanel.cpp | 4 +- src/widgets/Ruler.cpp | 326 ++++++------------ src/widgets/Ruler.h | 69 +--- src/widgets/Updater.cpp | 211 +++++++++--- src/widgets/Updater.h | 92 ++++- 21 files changed, 459 insertions(+), 437 deletions(-) diff --git a/src/AdornedRulerPanel.cpp b/src/AdornedRulerPanel.cpp index aadcdbe1542d..2ba4ef096513 100644 --- a/src/AdornedRulerPanel.cpp +++ b/src/AdornedRulerPanel.cpp @@ -1282,9 +1282,9 @@ AdornedRulerPanel::AdornedRulerPanel(AudacityProject* project, mOuter = GetClientRect(); - mRuler.SetUpdater( std::make_unique( mRuler, mViewInfo ), mLeftOffset ); + mRuler.SetUpdater( std::make_unique( mViewInfo ), mLeftOffset ); mRuler.SetLabelEdges( false ); - mRuler.SetFormat( Ruler::TimeFormat ); + mRuler.SetFormat( TimeFormat ); mTracks = &TrackList::Get( *project ); @@ -2579,7 +2579,7 @@ int AdornedRulerPanel::GetRulerHeight(bool showScrubBar) void AdornedRulerPanel::SetLeftOffset(int offset) { mLeftOffset = offset; - mRuler.SetUpdater( std::make_unique( mRuler, mViewInfo ), offset ); + mRuler.SetUpdater( std::make_unique( mViewInfo ), offset ); } // Draws the scrubbing/seeking indicator. diff --git a/src/FreqWindow.cpp b/src/FreqWindow.cpp index 071ec4397922..8ae30e64ae36 100644 --- a/src/FreqWindow.cpp +++ b/src/FreqWindow.cpp @@ -295,7 +295,7 @@ void FrequencyPlotDialog::Populate() S.GetParent(), wxID_ANY, wxVERTICAL, wxSize{ 100, 100 }, // Ruler can't handle small sizes RulerPanel::Range{ 0.0, -dBRange }, - Ruler::LinearDBFormat, + LinearDBFormat, XO("dB"), RulerPanel::Options{} .LabelEdges(true) @@ -378,7 +378,7 @@ void FrequencyPlotDialog::Populate() S.GetParent(), wxID_ANY, wxHORIZONTAL, wxSize{ 100, 100 }, // Ruler can't handle small sizes RulerPanel::Range{ 10, 20000 }, - Ruler::RealFormat, + RealFormat, XO("Hz"), RulerPanel::Options{} .Log(true) @@ -676,10 +676,10 @@ void FrequencyPlotDialog::DrawPlot() if (!mData || mDataLen < mWindowSize || mAnalyst->GetProcessedSize() == 0) { wxMemoryDC memDC; - vRuler->ruler.SetUpdater(std::make_unique(vRuler->ruler, nullptr)); + vRuler->ruler.SetUpdater(std::make_unique()); vRuler->ruler.SetRange(0.0, -dBRange); - hRuler->ruler.SetUpdater(std::make_unique(hRuler->ruler, nullptr)); + hRuler->ruler.SetUpdater(std::make_unique()); hRuler->ruler.SetRange(0, 1); DrawBackground(memDC); @@ -716,10 +716,10 @@ void FrequencyPlotDialog::DrawPlot() if (mAlg == SpectrumAnalyst::Spectrum) { vRuler->ruler.SetUnits(XO("dB")); - vRuler->ruler.SetFormat(Ruler::LinearDBFormat); + vRuler->ruler.SetFormat(LinearDBFormat); } else { vRuler->ruler.SetUnits({}); - vRuler->ruler.SetFormat(Ruler::RealFormat); + vRuler->ruler.SetFormat(RealFormat); } int w1, w2, h; vRuler->ruler.GetMaxSize(&w1, &h); @@ -754,19 +754,19 @@ void FrequencyPlotDialog::DrawPlot() if (mLogAxis) { xStep = pow(2.0f, (log(xRatio) / log(2.0f)) / width); - hRuler->ruler.SetUpdater(std::make_unique(hRuler->ruler, nullptr)); + hRuler->ruler.SetUpdater(std::make_unique()); } else { xStep = (xMax - xMin) / width; - hRuler->ruler.SetUpdater(std::make_unique(hRuler->ruler, nullptr)); + hRuler->ruler.SetUpdater(std::make_unique()); } hRuler->ruler.SetUnits(XO("Hz")); } else { xMin = 0; xMax = mAnalyst->GetProcessedSize() / mRate; xStep = (xMax - xMin) / width; - hRuler->ruler.SetUpdater(std::make_unique(hRuler->ruler, nullptr)); + hRuler->ruler.SetUpdater(std::make_unique()); /* i18n-hint: short form of 'seconds'.*/ hRuler->ruler.SetUnits(XO("s")); } diff --git a/src/Printing.cpp b/src/Printing.cpp index 98797879c708..b72d553b24e8 100644 --- a/src/Printing.cpp +++ b/src/Printing.cpp @@ -83,7 +83,7 @@ bool AudacityPrintout::OnPrintPage(int WXUNUSED(page)) ruler.SetBounds(0, 0, width, rulerPageHeight); ruler.SetOrientation(wxHORIZONTAL); ruler.SetRange(0.0, mTracks->GetEndTime()); - ruler.SetFormat(Ruler::TimeFormat); + ruler.SetFormat(TimeFormat); ruler.SetLabelEdges(true); ruler.Draw(*dc); diff --git a/src/TimeTrack.cpp b/src/TimeTrack.cpp index 6cd433ac57f8..2d74e0708181 100644 --- a/src/TimeTrack.cpp +++ b/src/TimeTrack.cpp @@ -75,9 +75,9 @@ void TimeTrack::CleanState() SetName(GetDefaultName()); mRuler = std::make_unique(); - mRuler->SetUpdater(std::make_unique(*(mRuler.get()), mZoomInfo), 0); + mRuler->SetUpdater(std::make_unique( mZoomInfo ), 0); mRuler->SetLabelEdges(false); - mRuler->SetFormat(Ruler::TimeFormat); + mRuler->SetFormat(TimeFormat); } TimeTrack::TimeTrack(const TimeTrack &orig, ProtectedCreationArg &&a, @@ -103,9 +103,9 @@ TimeTrack::TimeTrack(const TimeTrack &orig, ProtectedCreationArg &&a, ///@TODO: Give Ruler:: a copy-constructor instead of this? mRuler = std::make_unique(); - mRuler->SetUpdater(std::make_unique(*(mRuler.get()), mZoomInfo), 0); + mRuler->SetUpdater(std::make_unique( mZoomInfo ), 0); mRuler->SetLabelEdges(false); - mRuler->SetFormat(Ruler::TimeFormat); + mRuler->SetFormat(TimeFormat); } // Copy the track metadata but not the contents. diff --git a/src/effects/Compressor.cpp b/src/effects/Compressor.cpp index 71a800847a24..fc3b372ac925 100644 --- a/src/effects/Compressor.cpp +++ b/src/effects/Compressor.cpp @@ -649,7 +649,7 @@ void EffectCompressorPanel::OnPaint(wxPaintEvent & WXUNUSED(evt)) vRuler.SetBounds(0, 0, width, height); vRuler.SetOrientation(wxVERTICAL); vRuler.SetRange(0, -rangeDB); - vRuler.SetFormat(Ruler::LinearDBFormat); + vRuler.SetFormat(LinearDBFormat); vRuler.SetUnits(XO("dB")); vRuler.GetMaxSize(&w, NULL); @@ -657,7 +657,7 @@ void EffectCompressorPanel::OnPaint(wxPaintEvent & WXUNUSED(evt)) hRuler.SetBounds(0, 0, width, height); hRuler.SetOrientation(wxHORIZONTAL); hRuler.SetRange(-rangeDB, 0); - hRuler.SetFormat(Ruler::LinearDBFormat); + hRuler.SetFormat(LinearDBFormat); hRuler.SetUnits(XO("dB")); hRuler.SetFlip(true); hRuler.GetMaxSize(NULL, &h); diff --git a/src/effects/Equalization.cpp b/src/effects/Equalization.cpp index e49463a475d2..255590620ff8 100644 --- a/src/effects/Equalization.cpp +++ b/src/effects/Equalization.cpp @@ -724,7 +724,7 @@ std::unique_ptr EffectEqualization::PopulateOrExchange( S.GetParent(), wxID_ANY, wxVERTICAL, wxSize{ 100, 100 }, // Ruler can't handle small sizes RulerPanel::Range{ 60.0, -120.0 }, - Ruler::LinearDBFormat, + LinearDBFormat, XO("dB"), RulerPanel::Options{} .LabelEdges(true) @@ -781,7 +781,7 @@ std::unique_ptr EffectEqualization::PopulateOrExchange( S.GetParent(), wxID_ANY, wxHORIZONTAL, wxSize{ 100, 100 }, // Ruler can't handle small sizes RulerPanel::Range{ mLoFreq, mHiFreq }, - Ruler::IntFormat, + IntFormat, XO("Hz"), RulerPanel::Options{} .Log(true) @@ -2290,7 +2290,7 @@ void EffectEqualization::UpdateDraw() { EnvLogToLin(); mEnvelope = mLinEnvelope.get(); - mFreqRuler->ruler.SetUpdater(std::make_unique(mFreqRuler->ruler, nullptr)); + mFreqRuler->ruler.SetUpdater(std::make_unique()); mFreqRuler->ruler.SetRange(0, mHiFreq); } @@ -2322,7 +2322,7 @@ void EffectEqualization::UpdateGraphic() EnvLinToLog(); mEnvelope = mLogEnvelope.get(); - mFreqRuler->ruler.SetUpdater(std::make_unique(mFreqRuler->ruler, nullptr)); + mFreqRuler->ruler.SetUpdater(std::make_unique()); mFreqRuler->ruler.SetRange(mLoFreq, mHiFreq); } @@ -2911,7 +2911,7 @@ void EffectEqualization::OnLinFreq(wxCommandEvent & WXUNUSED(event)) mLin = mLinFreq->IsChecked(); if(IsLinear()) //going from log to lin freq scale { - mFreqRuler->ruler.SetUpdater(std::make_unique(mFreqRuler->ruler, nullptr)); + mFreqRuler->ruler.SetUpdater(std::make_unique()); mFreqRuler->ruler.SetRange(0, mHiFreq); EnvLogToLin(); mEnvelope = mLinEnvelope.get(); @@ -2919,7 +2919,7 @@ void EffectEqualization::OnLinFreq(wxCommandEvent & WXUNUSED(event)) } else //going from lin to log freq scale { - mFreqRuler->ruler.SetUpdater(std::make_unique(mFreqRuler->ruler, nullptr)); + mFreqRuler->ruler.SetUpdater(std::make_unique()); mFreqRuler->ruler.SetRange(mLoFreq, mHiFreq); EnvLinToLog(); mEnvelope = mLogEnvelope.get(); diff --git a/src/effects/ScienFilter.cpp b/src/effects/ScienFilter.cpp index b6a79b99a34f..96974063d2a9 100644 --- a/src/effects/ScienFilter.cpp +++ b/src/effects/ScienFilter.cpp @@ -278,7 +278,7 @@ std::unique_ptr EffectScienFilter::PopulateOrExchange( S.GetParent(), wxID_ANY, wxVERTICAL, wxSize{ 100, 100 }, // Ruler can't handle small sizes RulerPanel::Range{ 30.0, -120.0 }, - Ruler::LinearDBFormat, + LinearDBFormat, XO("dB"), RulerPanel::Options{} .LabelEdges(true) @@ -336,7 +336,7 @@ std::unique_ptr EffectScienFilter::PopulateOrExchange( S.GetParent(), wxID_ANY, wxHORIZONTAL, wxSize{ 100, 100 }, // Ruler can't handle small sizes RulerPanel::Range{ mLoFreq, mNyquist }, - Ruler::IntFormat, + IntFormat, {}, RulerPanel::Options{} .Log(true) diff --git a/src/tracks/playabletrack/wavetrack/ui/SpectrumVRulerControls.cpp b/src/tracks/playabletrack/wavetrack/ui/SpectrumVRulerControls.cpp index fe533718fe52..3d4ad076a573 100644 --- a/src/tracks/playabletrack/wavetrack/ui/SpectrumVRulerControls.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/SpectrumVRulerControls.cpp @@ -156,7 +156,7 @@ void SpectrumVRulerControls::DoUpdateVRuler( */ vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1); vruler->SetOrientation(wxVERTICAL); - vruler->SetFormat(Ruler::RealFormat); + vruler->SetFormat(RealFormat); vruler->SetLabelEdges(true); // use kHz in scale, if appropriate if (maxFreq >= 2000) { @@ -169,7 +169,7 @@ void SpectrumVRulerControls::DoUpdateVRuler( vruler->SetRange((int)(maxFreq), (int)(minFreq)); vruler->SetUnits({}); } - vruler->SetUpdater(std::make_unique(*vruler, nullptr)); + vruler->SetUpdater(std::make_unique()); } break; case SpectrogramSettings::stLogarithmic: @@ -187,11 +187,11 @@ void SpectrumVRulerControls::DoUpdateVRuler( */ vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1); vruler->SetOrientation(wxVERTICAL); - vruler->SetFormat(Ruler::IntFormat); + vruler->SetFormat(IntFormat); vruler->SetLabelEdges(true); vruler->SetRange(maxFreq, minFreq); vruler->SetUnits({}); - vruler->SetUpdater(std::make_unique(*vruler, nullptr)); + vruler->SetUpdater(std::make_unique()); NumberScale scale( wt->GetSpectrogramSettings().GetScale( minFreq, maxFreq ) .Reversal() ); diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveformVRulerControls.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveformVRulerControls.cpp index af2f7acd585f..26e6dc522293 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveformVRulerControls.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveformVRulerControls.cpp @@ -220,10 +220,10 @@ void WaveformVRulerControls::DoUpdateVRuler( vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1); vruler->SetOrientation(wxVERTICAL); vruler->SetRange(max, min); - vruler->SetFormat(Ruler::RealFormat); + vruler->SetFormat(RealFormat); vruler->SetUnits({}); vruler->SetLabelEdges(false); - vruler->SetUpdater(std::make_unique(*vruler, nullptr)); + vruler->SetUpdater(std::make_unique()); } else { wxASSERT(scaleType == WaveformSettings::stLogarithmic); @@ -329,9 +329,9 @@ void WaveformVRulerControls::DoUpdateVRuler( else vruler->SetBounds(0.0, 0.0, 0.0, 0.0); // A.C.H I couldn't find a way to just disable it? #endif - vruler->SetFormat(Ruler::RealLogFormat); + vruler->SetFormat(RealLogFormat); vruler->SetLabelEdges(true); - vruler->SetUpdater(std::make_unique(*vruler, nullptr)); + vruler->SetUpdater(std::make_unique()); } vruler->GetMaxSize( &wt->vrulerSize.first, &wt->vrulerSize.second ); } diff --git a/src/tracks/timetrack/ui/TimeTrackVRulerControls.cpp b/src/tracks/timetrack/ui/TimeTrackVRulerControls.cpp index 541500d1e2fd..ff7be16e0fb6 100644 --- a/src/tracks/timetrack/ui/TimeTrackVRulerControls.cpp +++ b/src/tracks/timetrack/ui/TimeTrackVRulerControls.cpp @@ -119,13 +119,13 @@ void TimeTrackVRulerControls::UpdateRuler( const wxRect &rect ) vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height-1); vruler->SetOrientation(wxVERTICAL); vruler->SetRange(max, min); - vruler->SetFormat((tt->GetDisplayLog()) ? Ruler::RealLogFormat : Ruler::RealFormat); + vruler->SetFormat((tt->GetDisplayLog()) ? RealLogFormat : RealFormat); vruler->SetUnits({}); vruler->SetLabelEdges(false); if (tt->GetDisplayLog()) - vruler->SetUpdater(std::make_unique(*vruler, nullptr)); + vruler->SetUpdater(std::make_unique()); else - vruler->SetUpdater(std::make_unique(*vruler, nullptr)); + vruler->SetUpdater(std::make_unique()); vruler->GetMaxSize( &tt->vrulerSize.first, &tt->vrulerSize.second ); } diff --git a/src/widgets/CustomUpdater.cpp b/src/widgets/CustomUpdater.cpp index f4b74520f264..7bad6b6a55b7 100644 --- a/src/widgets/CustomUpdater.cpp +++ b/src/widgets/CustomUpdater.cpp @@ -12,10 +12,11 @@ #include "CustomUpdater.h" void CustomUpdater::Update( - wxDC& dc, const Envelope* envelope, UpdateOutputs& allOutputs) const + wxDC& dc, const Envelope* envelope, + UpdateOutputs& allOutputs, const RulerStruct& context) const { - const int mLength = mRuler.mLength; - const Ruler::Fonts& mFonts = *mRuler.mpFonts; + const int mLength = context.mLength; + const RulerStruct::Fonts& mFonts = *context.mpFonts; TickOutputs majorOutputs{ allOutputs.majorLabels, allOutputs.bits, allOutputs.box }; @@ -26,7 +27,9 @@ void CustomUpdater::Update( int numLabel = allOutputs.majorLabels.size(); for (int i = 0; (i < numLabel) && (i <= mLength); ++i) - TickCustom(dc, i, mFonts.major, majorOutputs); + TickCustom(dc, i, mFonts.major, majorOutputs, context); - BoxAdjust(allOutputs); + BoxAdjust(allOutputs, context); } + +CustomUpdater::~CustomUpdater() = default; diff --git a/src/widgets/CustomUpdater.h b/src/widgets/CustomUpdater.h index 760f6f728e42..42003719fcee 100644 --- a/src/widgets/CustomUpdater.h +++ b/src/widgets/CustomUpdater.h @@ -14,13 +14,14 @@ #include "Updater.h" struct CustomUpdater : public Updater { - explicit CustomUpdater(const Ruler& ruler, const ZoomInfo* z) - : Updater{ ruler, NULL } + explicit CustomUpdater(const ZoomInfo* z = nullptr) + : Updater{ z } {} + ~CustomUpdater() override; void Update( wxDC& dc, const Envelope* envelope, - UpdateOutputs& allOutputs + UpdateOutputs& allOutputs, const RulerStruct& context ) const override; }; diff --git a/src/widgets/LinearUpdater.cpp b/src/widgets/LinearUpdater.cpp index 9251c08c0fb2..b68757255f32 100644 --- a/src/widgets/LinearUpdater.cpp +++ b/src/widgets/LinearUpdater.cpp @@ -11,29 +11,30 @@ #include "LinearUpdater.h" void LinearUpdater::Update( - wxDC& dc, const Envelope* envelope, UpdateOutputs& allOutputs) const + wxDC& dc, const Envelope* envelope, + UpdateOutputs& allOutputs, const RulerStruct& context) const { TickOutputs majorOutputs{ allOutputs.majorLabels, allOutputs.bits, allOutputs.box }; - const double mDbMirrorValue = mRuler.mDbMirrorValue; - const int mLength = mRuler.mLength; - const Ruler::RulerFormat mFormat = mRuler.mFormat; + const double mDbMirrorValue = context.mDbMirrorValue; + const int mLength = context.mLength; + const RulerFormat mFormat = context.mFormat; - const int mLeft = mRuler.mLeft; - const int mTop = mRuler.mTop; - const int mBottom = mRuler.mBottom; - const int mRight = mRuler.mRight; - const int mOrientation = mRuler.mOrientation; + const int mLeft = context.mLeft; + const int mTop = context.mTop; + const int mBottom = context.mBottom; + const int mRight = context.mRight; + const int mOrientation = context.mOrientation; - const double mMin = mRuler.mMin; - const double mMax = mRuler.mMax; - const double mHiddenMin = mRuler.mHiddenMin; - const double mHiddenMax = mRuler.mHiddenMax; + const double mMin = context.mMin; + const double mMax = context.mMax; + const double mHiddenMin = context.mHiddenMin; + const double mHiddenMax = context.mHiddenMax; - const Ruler::Fonts& mFonts = *mRuler.mpFonts; - const bool mLabelEdges = mRuler.mLabelEdges; - const int mLeftOffset = mRuler.mLeftOffset; + const RulerStruct::Fonts& mFonts = *context.mpFonts; + const bool mLabelEdges = context.mLabelEdges; + const int mLeftOffset = context.mLeftOffset; // Use the "hidden" min and max to determine the tick size. // That may make a difference with fisheye. @@ -44,7 +45,7 @@ void LinearUpdater::Update( auto TickAtValue = [this, &tickSizes, &dc, &majorOutputs, &mFonts, mOrientation, - mMin, mMax, mLength, mLeftOffset, mRight, mBottom] + mMin, mMax, mLength, mLeftOffset, mRight, mBottom, &context] (double value) -> int { // Make a tick only if the value is strictly between the bounds if (value <= std::min(mMin, mMax)) @@ -64,7 +65,7 @@ void LinearUpdater::Update( const int iMaxPos = (mOrientation == wxHORIZONTAL) ? mRight : mBottom - 5; if (mid >= 0 && mid < iMaxPos) - Tick(dc, mid, value, tickSizes, mFonts.major, majorOutputs); + Tick(dc, mid, value, tickSizes, mFonts.major, majorOutputs, context); else return -1; @@ -87,8 +88,8 @@ void LinearUpdater::Update( // Extreme values if (mLabelEdges) { - Tick(dc, 0, mMin, tickSizes, mFonts.major, majorOutputs); - Tick(dc, mLength, mMax, tickSizes, mFonts.major, majorOutputs); + Tick(dc, 0, mMin, tickSizes, mFonts.major, majorOutputs, context); + Tick(dc, mLength, mMax, tickSizes, mFonts.major, majorOutputs, context); } if (!mDbMirrorValue) { @@ -148,7 +149,7 @@ void LinearUpdater::Update( bool major = jj == 0; tickSizes.useMajor = major; bool ticked = Tick(dc, ii, sign * step * denom, tickSizes, - font, outputs); + font, outputs, context); if (!major && !ticked) { nDroppedMinorLabels++; } @@ -172,9 +173,11 @@ void LinearUpdater::Update( // Left and Right Edges if (mLabelEdges) { - Tick(dc, 0, mMin, tickSizes, mFonts.major, majorOutputs); - Tick(dc, mLength, mMax, tickSizes, mFonts.major, majorOutputs); + Tick(dc, 0, mMin, tickSizes, mFonts.major, majorOutputs, context); + Tick(dc, mLength, mMax, tickSizes, mFonts.major, majorOutputs, context); } - BoxAdjust(allOutputs); + BoxAdjust(allOutputs, context); } + +LinearUpdater::~LinearUpdater() = default; diff --git a/src/widgets/LinearUpdater.h b/src/widgets/LinearUpdater.h index e35a49a6e7e5..09b8455e722d 100644 --- a/src/widgets/LinearUpdater.h +++ b/src/widgets/LinearUpdater.h @@ -14,13 +14,14 @@ #include "Updater.h" struct LinearUpdater : public Updater { - explicit LinearUpdater(const Ruler& ruler, const ZoomInfo* z) - : Updater{ ruler, z } + explicit LinearUpdater(const ZoomInfo* z = nullptr) + : Updater{ z } {} + ~LinearUpdater() override; void Update( wxDC& dc, const Envelope* envelope, - UpdateOutputs& allOutputs + UpdateOutputs& allOutputs, const RulerStruct& context ) const override; }; diff --git a/src/widgets/LogarithmicUpdater.cpp b/src/widgets/LogarithmicUpdater.cpp index 8a3f43424b6c..0a1bc58d189c 100644 --- a/src/widgets/LogarithmicUpdater.cpp +++ b/src/widgets/LogarithmicUpdater.cpp @@ -11,23 +11,24 @@ #include "LogarithmicUpdater.h" void LogarithmicUpdater::Update( - wxDC& dc, const Envelope* envelope, UpdateOutputs& allOutputs) const + wxDC& dc, const Envelope* envelope, + UpdateOutputs& allOutputs, const RulerStruct& context) const { TickOutputs majorOutputs{ allOutputs.majorLabels, allOutputs.bits, allOutputs.box }; - const int mLength = mRuler.mLength; - const Ruler::RulerFormat mFormat = mRuler.mFormat; + const int mLength = context.mLength; + const RulerFormat mFormat = context.mFormat; - const int mOrientation = mRuler.mOrientation; + const int mOrientation = context.mOrientation; - const double mMin = mRuler.mMin; - const double mMax = mRuler.mMax; - const double mHiddenMin = mRuler.mHiddenMin; - const double mHiddenMax = mRuler.mHiddenMax; + const double mMin = context.mMin; + const double mMax = context.mMax; + const double mHiddenMin = context.mHiddenMin; + const double mHiddenMax = context.mHiddenMax; - const Ruler::Fonts& mFonts = *mRuler.mpFonts; - const NumberScale mNumberScale = mRuler.mNumberScale; + const RulerStruct::Fonts& mFonts = *context.mpFonts; + const NumberScale mNumberScale = context.mNumberScale; auto numberScale = (mNumberScale == NumberScale{}) ? NumberScale(nstLogarithmic, mMin, mMax) @@ -55,7 +56,7 @@ void LogarithmicUpdater::Update( { val = decade; if (val >= rMin && val < rMax) { const int pos(0.5 + mLength * numberScale.ValueToPosition(val)); - Tick(dc, pos, val, tickSizes, mFonts.major, majorOutputs); + Tick(dc, pos, val, tickSizes, mFonts.major, majorOutputs, context); } } decade *= step; @@ -81,7 +82,7 @@ void LogarithmicUpdater::Update( val = decade * j; if (val >= rMin && val < rMax) { const int pos(0.5 + mLength * numberScale.ValueToPosition(val)); - Tick(dc, pos, val, tickSizes, mFonts.minor, minorOutputs); + Tick(dc, pos, val, tickSizes, mFonts.minor, minorOutputs, context); } } decade *= step; @@ -102,14 +103,14 @@ void LogarithmicUpdater::Update( allOutputs.minorMinorLabels, allOutputs.bits, allOutputs.box }; for (int i = 0; i <= steps; i++) { // PRL: Bug1038. Don't label 1.6, rounded, as a duplicate tick for "2" - if (!(mFormat == Ruler::IntFormat && decade < 10.0)) { + if (!(mFormat == IntFormat && decade < 10.0)) { for (int f = start; f != (int)(end); f += mstep) { if ((int)(f / 10) != f / 10.0f) { val = decade * f / 10; if (val >= rMin && val < rMax) { const int pos(0.5 + mLength * numberScale.ValueToPosition(val)); Tick(dc, pos, val, tickSizes, - mFonts.minorMinor, minorMinorOutputs); + mFonts.minorMinor, minorMinorOutputs, context); } } } @@ -117,5 +118,7 @@ void LogarithmicUpdater::Update( decade *= step; } - BoxAdjust(allOutputs); + BoxAdjust(allOutputs, context); } + +LogarithmicUpdater::~LogarithmicUpdater() = default; diff --git a/src/widgets/LogarithmicUpdater.h b/src/widgets/LogarithmicUpdater.h index bf02d0e92674..ab3982e5b590 100644 --- a/src/widgets/LogarithmicUpdater.h +++ b/src/widgets/LogarithmicUpdater.h @@ -14,13 +14,14 @@ #include "Updater.h" struct LogarithmicUpdater : public Updater { - explicit LogarithmicUpdater(const Ruler& ruler, const ZoomInfo* z) - : Updater{ ruler, NULL } + explicit LogarithmicUpdater(const ZoomInfo* z = nullptr) + : Updater{ z } {} + ~LogarithmicUpdater() override; void Update( wxDC& dc, const Envelope* envelope, - UpdateOutputs& allOutputs + UpdateOutputs& allOutputs, const RulerStruct& context ) const override; }; diff --git a/src/widgets/MeterPanel.cpp b/src/widgets/MeterPanel.cpp index 01d4ea9eb512..4da9373242f9 100644 --- a/src/widgets/MeterPanel.cpp +++ b/src/widgets/MeterPanel.cpp @@ -1264,7 +1264,7 @@ void MeterPanel::SetActiveStyle(Style newStyle) if (mDB) { - mRuler.SetFormat(Ruler::LinearDBFormat); + mRuler.SetFormat(LinearDBFormat); if (mStyle == HorizontalStereo || mStyle == HorizontalStereoCompact) { mRuler.SetOrientation(wxHORIZONTAL); @@ -1278,7 +1278,7 @@ void MeterPanel::SetActiveStyle(Style newStyle) } else { - mRuler.SetFormat(Ruler::RealFormat); + mRuler.SetFormat(RealFormat); if (mStyle == HorizontalStereo || mStyle == HorizontalStereoCompact) { mRuler.SetOrientation(wxHORIZONTAL); diff --git a/src/widgets/Ruler.cpp b/src/widgets/Ruler.cpp index 9aa6314ca3ab..bf8787306109 100644 --- a/src/widgets/Ruler.cpp +++ b/src/widgets/Ruler.cpp @@ -84,25 +84,12 @@ using std::max; Ruler::Ruler() { - mMin = mHiddenMin = 0.0; - mMax = mHiddenMax = 100.0; - mOrientation = wxHORIZONTAL; - mSpacing = 6; mHasSetSpacing = false; - mFormat = RealFormat; - mFlip = false; - mLog = false; - mLabelEdges = false; - - mLeft = -1; - mTop = -1; - mRight = -1; - mBottom = -1; + mbTicksOnly = true; mbTicksAtExtremes = false; mTickColour = wxColour( theTheme.Colour( clrTrackPanelText )); mPen.SetColour(mTickColour); - mDbMirrorValue = 0.0; // Note: the font size is now adjusted automatically whenever // Invalidate is called on a horizontal Ruler, unless the user @@ -114,9 +101,6 @@ Ruler::Ruler() fontSize = 8; #endif - mLength = 0; - - mCustom = false; mbMinor = true; mTwoTone = false; @@ -126,7 +110,7 @@ Ruler::Ruler() // This part in particular needs inspection, not giving an error // But is this corret? And should it be set to NULL or nullptr if a default // cannot be made? - mpUpdater = std::make_unique( *this, mUseZoomInfo ); + mpUpdater = std::make_unique( mUseZoomInfo ); // mpUpdater = nullptr; } @@ -144,8 +128,8 @@ void Ruler::SetFormat(RulerFormat format) { // IntFormat, RealFormat, RealLogFormat, TimeFormat, or LinearDBFormat - if (mFormat != format) { - mFormat = format; + if (mRulerStruct.mFormat != format) { + mRulerStruct.mFormat = format; Invalidate(); } @@ -166,8 +150,8 @@ void Ruler::SetUpdater // Runtime type comparison isn't clean in c++ mpUpdater = std::move(pUpdater); - if (mLeftOffset != leftOffset) - mLeftOffset = leftOffset; + if (mRulerStruct.mLeftOffset != leftOffset) + mRulerStruct.mLeftOffset = leftOffset; // Hm, is this invalidation sufficient? What if *zoomInfo changes under us? if (mUseZoomInfo != mpUpdater->zoomInfo) @@ -181,8 +165,8 @@ void Ruler::SetUnits(const TranslatableString &units) // Specify the name of the units (like "dB") if you // want numbers like "1.6" formatted as "1.6 dB". - if (mUnits != units) { - mUnits = units; + if (mRulerStruct.mUnits != units) { + mRulerStruct.mUnits = units; Invalidate(); } @@ -190,8 +174,8 @@ void Ruler::SetUnits(const TranslatableString &units) void Ruler::SetDbMirrorValue( const double d ) { - if (mDbMirrorValue != d) { - mDbMirrorValue = d; + if (mRulerStruct.mDbMirrorValue != d) { + mRulerStruct.mDbMirrorValue = d; Invalidate(); } @@ -201,11 +185,11 @@ void Ruler::SetOrientation(int orient) { // wxHORIZONTAL || wxVERTICAL - if (mOrientation != orient) { - mOrientation = orient; + if (mRulerStruct.mOrientation != orient) { + mRulerStruct.mOrientation = orient; - if (mOrientation == wxVERTICAL && !mHasSetSpacing) - mSpacing = 2; + if (mRulerStruct.mOrientation == wxVERTICAL && !mHasSetSpacing) + mRulerStruct.mSpacing = 2; Invalidate(); } @@ -227,12 +211,12 @@ void Ruler::SetRange // hiddenMin and hiddenMax are values that would be shown with the fisheye // turned off. In other cases they equal min and max respectively. - if (mMin != min || mMax != max || - mHiddenMin != hiddenMin || mHiddenMax != hiddenMax) { - mMin = min; - mMax = max; - mHiddenMin = hiddenMin; - mHiddenMax = hiddenMax; + if (mRulerStruct.mMin != min || mRulerStruct.mMax != max || + mRulerStruct.mHiddenMin != hiddenMin || mRulerStruct.mHiddenMax != hiddenMax) { + mRulerStruct.mMin = min; + mRulerStruct.mMax = max; + mRulerStruct.mHiddenMin = hiddenMin; + mRulerStruct.mHiddenMax = hiddenMax; Invalidate(); } @@ -242,8 +226,8 @@ void Ruler::SetSpacing(int spacing) { mHasSetSpacing = true; - if (mSpacing != spacing) { - mSpacing = spacing; + if (mRulerStruct.mSpacing != spacing) { + mRulerStruct.mSpacing = spacing; Invalidate(); } @@ -255,8 +239,8 @@ void Ruler::SetLabelEdges(bool labelEdges) // receive a label. If not, the nearest round number is // labeled (which may or may not be the edge). - if (mLabelEdges != labelEdges) { - mLabelEdges = labelEdges; + if (mRulerStruct.mLabelEdges != labelEdges) { + mRulerStruct.mLabelEdges = labelEdges; Invalidate(); } @@ -268,8 +252,8 @@ void Ruler::SetFlip(bool flip) // is reversed from the default; eg. above the line // instead of below - if (mFlip != flip) { - mFlip = flip; + if (mRulerStruct.mFlip != flip) { + mRulerStruct.mFlip = flip; Invalidate(); } @@ -305,39 +289,39 @@ void Ruler::SetFonts(const wxFont &minorFont, const wxFont &majorFont, const wxF { // Won't override these fonts - mpUserFonts = std::make_unique( - Fonts{ majorFont, minorFont, minorMinorFont, 0 } ); + mpUserFonts = std::make_unique( + RulerStruct::Fonts{ majorFont, minorFont, minorMinorFont, 0 } ); wxScreenDC dc; wxCoord height; FindFontHeights( height, mpUserFonts->lead, dc, majorFont ); - mpFonts.reset(); - mpFonts.reset(); + mRulerStruct.mpFonts.reset(); + mRulerStruct.mpFonts.reset(); Invalidate(); } void Ruler::SetNumberScale(const NumberScale &scale) { - if ( mNumberScale != scale ) { - mNumberScale = scale; + if ( mRulerStruct.mNumberScale != scale ) { + mRulerStruct.mNumberScale = scale; Invalidate(); } } void Ruler::OfflimitsPixels(int start, int end) { - int length = mLength; - if (mOrientation == wxHORIZONTAL) - length = mRight - mLeft; + int length = mRulerStruct.mLength; + if (mRulerStruct.mOrientation == wxHORIZONTAL) + length = mRulerStruct.mRight - mRulerStruct.mLeft; else - length = mBottom - mTop; + length = mRulerStruct.mBottom - mRulerStruct.mTop; if( length < 0 ) return; auto size = static_cast( length + 1 ); if ( mUserBits.size() < size ) { - mLength = length; + mRulerStruct.mLength = length; mUserBits.resize( size, false ); } @@ -346,8 +330,8 @@ void Ruler::OfflimitsPixels(int start, int end) if (start < 0) start = 0; - if (end > mLength) - end = mLength; + if (end > mRulerStruct.mLength) + end = mRulerStruct.mLength; for(int i = start; i <= end; i++) mUserBits[i] = true; @@ -357,12 +341,12 @@ void Ruler::OfflimitsPixels(int start, int end) void Ruler::SetBounds(int left, int top, int right, int bottom) { - if (mLeft != left || mTop != top || - mRight != right || mBottom != bottom) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; + if (mRulerStruct.mLeft != left || mRulerStruct.mTop != top || + mRulerStruct.mRight != right || mRulerStruct.mBottom != bottom) { + mRulerStruct.mLeft = left; + mRulerStruct.mTop = top; + mRulerStruct.mRight = right; + mRulerStruct.mBottom = bottom; Invalidate(); } @@ -370,110 +354,19 @@ void Ruler::SetBounds(int left, int top, int right, int bottom) void Ruler::Invalidate() { - if (mOrientation == wxHORIZONTAL) - mLength = mRight-mLeft; + if (mRulerStruct.mOrientation == wxHORIZONTAL) + mRulerStruct.mLength = mRulerStruct.mRight-mRulerStruct.mLeft; else - mLength = mBottom-mTop; + mRulerStruct.mLength = mRulerStruct.mBottom-mRulerStruct.mTop; mpCache.reset(); // Bug 2316 we must preserve off-limit pixels. // mUserBits.clear(); } -auto Ruler::MakeTick( - Label lab, - wxDC &dc, wxFont font, - std::vector &bits, - int left, int top, int spacing, int lead, - bool flip, int orientation ) - -> std::pair< wxRect, Label > -{ - lab.lx = left - 1000; // don't display - lab.ly = top - 1000; // don't display - - auto length = bits.size() - 1; - auto pos = lab.pos; - - dc.SetFont( font ); - - wxCoord strW, strH, strD, strL; - auto str = lab.text; - // Do not put the text into results until we are sure it does not overlap - lab.text = {}; - dc.GetTextExtent(str.Translation(), &strW, &strH, &strD, &strL); - - int strPos, strLen, strLeft, strTop; - if ( orientation == wxHORIZONTAL ) { - strLen = strW; - strPos = pos - strW/2; - if (strPos < 0) - strPos = 0; - if (strPos + strW >= length) - strPos = length - strW; - strLeft = left + strPos; - if ( flip ) - strTop = top + 4; - else - strTop = -strH - lead; -// strTop = top - lead + 4;// More space was needed... - } - else { - strLen = strH; - strPos = pos - strH/2; - if (strPos < 0) - strPos = 0; - if (strPos + strH >= length) - strPos = length - strH; - strTop = top + strPos; - if ( flip ) - strLeft = left + 5; - else - strLeft = -strW - 6; - } - - // FIXME: we shouldn't even get here if strPos < 0. - // Ruler code currently does not handle very small or - // negative sized windows (i.e. don't draw) properly. - if( strPos < 0 ) - return { {}, lab }; - - // See if any of the pixels we need to draw this - // label is already covered - - int i; - for(i=0; i length - spacing) - rightMargin = length - strPos - strLen; - strLen += rightMargin; - - for(i=0; i(*pUserFonts); + mRulerStruct.mpFonts = std::make_unique(*pUserFonts); return; } - mpFonts = std::make_unique(Fonts{ {}, {}, {}, 0 }); - auto& fonts = *mpFonts; + mRulerStruct.mpFonts = std::make_unique(RulerStruct::Fonts{ {}, {}, {}, 0 }); + auto& fonts = *(mRulerStruct.mpFonts); int fontSize = 4; @@ -552,42 +445,42 @@ void Ruler::UpdateCache( // Values of mLength of zero or below cause bad array allocations and // division by zero. So... // IF too small THEN bail out and don't draw. - if( mLength <= 0 ) + if( mRulerStruct.mLength <= 0 ) return; - if (mOrientation == wxHORIZONTAL) - cache.mRect = { 0, 0, mLength, 0 }; + if (mRulerStruct.mOrientation == wxHORIZONTAL) + cache.mRect = { 0, 0, mRulerStruct.mLength, 0 }; else - cache.mRect = { 0, 0, 0, mLength }; + cache.mRect = { 0, 0, 0, mRulerStruct.mLength }; // FIXME: Surely we do not need to allocate storage for the labels? // We can just recompute them as we need them? Yes, but only if // mCustom is false!!!! - if(!mCustom) { - cache.mMajorLabels.clear(); - cache.mMinorLabels.clear(); - cache.mMinorMinorLabels.clear(); - } + //if(!mCustom) { + // cache.mMajorLabels.clear(); + // cache.mMinorLabels.clear(); + // cache.mMinorMinorLabels.clear(); + //} cache.mBits = mUserBits; - cache.mBits.resize( static_cast(mLength + 1), false ); + cache.mBits.resize( static_cast(mRulerStruct.mLength + 1), false ); Updater::UpdateOutputs allOutputs{ cache.mMajorLabels, cache.mMinorLabels, cache.mMinorMinorLabels, cache.mBits, cache.mRect }; - mpUpdater->Update(dc, envelope, allOutputs); + mpUpdater->Update(dc, envelope, allOutputs, mRulerStruct); } -auto Ruler::GetFonts() const -> Fonts +auto Ruler::GetFonts() const -> RulerStruct::Fonts { - if ( !mpFonts ) { + if ( !mRulerStruct.mpFonts ) { wxScreenDC dc; ChooseFonts( dc ); } - return *mpFonts; + return *(mRulerStruct.mpFonts); } void Ruler::Draw(wxDC& dc) const @@ -597,7 +490,7 @@ void Ruler::Draw(wxDC& dc) const void Ruler::Draw(wxDC& dc, const Envelope* envelope) const { - if( mLength <=0 ) + if(mRulerStruct.mLength <=0 ) return; UpdateCache( dc, envelope ); @@ -613,53 +506,53 @@ void Ruler::Draw(wxDC& dc, const Envelope* envelope) const // Draws a long line the length of the ruler. if( !mbTicksOnly ) { - if (mOrientation == wxHORIZONTAL) { - if (mFlip) - AColor::Line(dc, mLeft, mTop, mRight, mTop); + if (mRulerStruct.mOrientation == wxHORIZONTAL) { + if (mRulerStruct.mFlip) + AColor::Line(dc, mRulerStruct.mLeft, mRulerStruct.mTop, mRulerStruct.mRight, mRulerStruct.mTop); else - AColor::Line(dc, mLeft, mBottom, mRight, mBottom); + AColor::Line(dc, mRulerStruct.mLeft, mRulerStruct.mBottom, mRulerStruct.mRight, mRulerStruct.mBottom); } else { - if (mFlip) - AColor::Line(dc, mLeft, mTop, mLeft, mBottom); + if (mRulerStruct.mFlip) + AColor::Line(dc, mRulerStruct.mLeft, mRulerStruct.mTop, mRulerStruct.mLeft, mRulerStruct.mBottom); else { // These calculations appear to be wrong, and to never have been used (so not tested) prior to MixerBoard. // AColor::Line(dc, mRect.x-mRect.width, mTop, mRect.x-mRect.width, mBottom); - const int nLineX = mRight - 1; - AColor::Line(dc, nLineX, mTop, nLineX, mBottom); + const int nLineX = mRulerStruct.mRight - 1; + AColor::Line(dc, nLineX, mRulerStruct.mTop, nLineX, mRulerStruct.mBottom); } } } - dc.SetFont( mpFonts->major ); + dc.SetFont( mRulerStruct.mpFonts->major ); // We may want to not show the ticks at the extremes, // though still showing the labels. // This gives a better look when the ruler is on a bevelled // button, since otherwise the tick is drawn on the bevel. - int iMaxPos = (mOrientation==wxHORIZONTAL)? mRight : mBottom-5; + int iMaxPos = (mRulerStruct.mOrientation==wxHORIZONTAL)? mRulerStruct.mRight : mRulerStruct.mBottom-5; - auto drawLabel = [this, iMaxPos, &dc]( const Label &label, int length ){ + auto drawLabel = [this, iMaxPos, &dc]( const Updater::Label &label, int length ){ int pos = label.pos; if( mbTicksAtExtremes || ((pos!=0)&&(pos!=iMaxPos))) { - if (mOrientation == wxHORIZONTAL) { - if (mFlip) - AColor::Line(dc, mLeft + pos, mTop, - mLeft + pos, mTop + length); + if (mRulerStruct.mOrientation == wxHORIZONTAL) { + if (mRulerStruct.mFlip) + AColor::Line(dc, mRulerStruct.mLeft + pos, mRulerStruct.mTop, + mRulerStruct.mLeft + pos, mRulerStruct.mTop + length); else - AColor::Line(dc, mLeft + pos, mBottom - length, - mLeft + pos, mBottom); + AColor::Line(dc, mRulerStruct.mLeft + pos, mRulerStruct.mBottom - length, + mRulerStruct.mLeft + pos, mRulerStruct.mBottom); } else { - if (mFlip) - AColor::Line(dc, mLeft, mTop + pos, - mLeft + length, mTop + pos); + if (mRulerStruct.mFlip) + AColor::Line(dc, mRulerStruct.mLeft, mRulerStruct.mTop + pos, + mRulerStruct.mLeft + length, mRulerStruct.mTop + pos); else - AColor::Line(dc, mRight - length, mTop + pos, - mRight, mTop + pos); + AColor::Line(dc, mRulerStruct.mRight - length, mRulerStruct.mTop + pos, + mRulerStruct.mRight, mRulerStruct.mTop + pos); } } @@ -670,12 +563,12 @@ void Ruler::Draw(wxDC& dc, const Envelope* envelope) const drawLabel( label, 4 ); if( mbMinor ) { - dc.SetFont( mpFonts->minor ); + dc.SetFont( mRulerStruct.mpFonts->minor ); for( const auto &label : cache.mMinorLabels ) drawLabel( label, 2 ); } - dc.SetFont( mpFonts->minorMinor ); + dc.SetFont( mRulerStruct.mpFonts->minorMinor ); for( const auto &label : cache.mMinorMinorLabels ) if ( !label.text.empty() ) @@ -699,7 +592,7 @@ void Ruler::DrawGrid(wxDC& dc, dc.SetPen(gridPen); for( const auto &label : cache.mMinorLabels ) { gridPos = label.pos; - if(mOrientation == wxHORIZONTAL) { + if(mRulerStruct.mOrientation == wxHORIZONTAL) { if((gridPos != 0) && (gridPos != gridLineLength)) AColor::Line(dc, gridPos+xOffset, yOffset, gridPos+xOffset, gridLineLength-1+yOffset); } @@ -715,7 +608,7 @@ void Ruler::DrawGrid(wxDC& dc, dc.SetPen(gridPen); for( const auto &label : cache.mMajorLabels ) { gridPos = label.pos; - if(mOrientation == wxHORIZONTAL) { + if(mRulerStruct.mOrientation == wxHORIZONTAL) { if((gridPos != 0) && (gridPos != gridLineLength)) AColor::Line(dc, gridPos+xOffset, yOffset, gridPos+xOffset, gridLineLength-1+yOffset); } @@ -729,7 +622,7 @@ void Ruler::DrawGrid(wxDC& dc, if(zeroPosition > 0) { // Draw 'zero' grid line in black dc.SetPen(*wxBLACK_PEN); - if(mOrientation == wxHORIZONTAL) { + if(mRulerStruct.mOrientation == wxHORIZONTAL) { if(zeroPosition != gridLineLength) AColor::Line(dc, zeroPosition+xOffset, yOffset, zeroPosition+xOffset, gridLineLength-1+yOffset); } @@ -741,10 +634,10 @@ void Ruler::DrawGrid(wxDC& dc, } } -int Ruler::FindZero( const Labels &labels ) const +int Ruler::FindZero( const Updater::Labels &labels ) const { auto begin = labels.begin(), end = labels.end(), - iter = std::find_if( begin, end, []( const Label &label ){ + iter = std::find_if( begin, end, []( const Updater::Label &label ){ return label.value == 0.0; } ); @@ -780,15 +673,6 @@ void Ruler::GetMaxSize(wxCoord *width, wxCoord *height) *height = cache.mRect.GetHeight(); } - -void Ruler::SetCustomMode(bool value) -{ - if ( mCustom != value ) { - mCustom = value; - Invalidate(); - } -} - #if 0 // These two unused functions need reconsideration of their interactions with // the cache and update @@ -827,7 +711,7 @@ void Ruler::SetCustomMinorLabels( } #endif -void Ruler::Label::Draw(wxDC&dc, bool twoTone, wxColour c) const +void Updater::Label::Draw(wxDC&dc, bool twoTone, wxColour c) const { if (!text.empty()) { bool altColor = twoTone && value < 0.0; @@ -858,7 +742,7 @@ RulerPanel::RulerPanel(wxWindow* parent, wxWindowID id, wxOrientation orientation, const wxSize &bounds, const Range &range, - Ruler::RulerFormat format, + RulerFormat format, const TranslatableString &units, const Options &options, const wxPoint& pos /*= wxDefaultPosition*/, @@ -869,9 +753,9 @@ RulerPanel::RulerPanel(wxWindow* parent, wxWindowID id, ruler.SetOrientation(orientation); ruler.SetRange( range.first, range.second ); if (options.log) - ruler.SetUpdater(std::make_unique(ruler, nullptr)); + ruler.SetUpdater(std::make_unique()); else - ruler.SetUpdater(std::make_unique(ruler, nullptr)); + ruler.SetUpdater(std::make_unique()); ruler.SetFormat(format); ruler.SetUnits( units ); ruler.SetFlip( options.flip ); diff --git a/src/widgets/Ruler.h b/src/widgets/Ruler.h index dcede69b6336..5b908acdfbeb 100644 --- a/src/widgets/Ruler.h +++ b/src/widgets/Ruler.h @@ -12,6 +12,7 @@ #define __AUDACITY_RULER__ #include "wxPanelWrapper.h" // to inherit +#include "Updater.h" // member variable #include "NumberScale.h" // member variable #include // member variable @@ -22,19 +23,10 @@ class wxFont; class Envelope; class ZoomInfo; -struct Updater; class AUDACITY_DLL_API Ruler { public: - enum RulerFormat { - IntFormat, - RealFormat, - RealLogFormat, - TimeFormat, - LinearDBFormat, - }; - // // Constructor / Destructor // @@ -102,11 +94,7 @@ class AUDACITY_DLL_API Ruler { // Good defaults are provided, but you can override here void SetFonts(const wxFont &minorFont, const wxFont &majorFont, const wxFont &minorMinorFont); - struct Fonts { - wxFont major, minor, minorMinor; - int lead; - }; - Fonts GetFonts() const; + RulerStruct::Fonts GetFonts() const; void SetNumberScale(const NumberScale &scale); @@ -155,23 +143,6 @@ class AUDACITY_DLL_API Ruler { void Invalidate(); private: - friend class Updater; - friend class LinearUpdater; - friend class LogarithmicUpdater; - friend class CustomUpdater; - - class Label { - public: - double value; - int pos; - int lx, ly; - TranslatableString text; - - void Draw(wxDC &dc, bool twoTone, wxColour c) const; - }; - using Labels = std::vector