diff --git a/src/Menus.cpp b/src/Menus.cpp index b969785ce..2352113de 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -1770,10 +1770,10 @@ wxUint32 AudacityProject::GetUpdateFlags() if (mUndoManager.RedoAvailable()) flags |= RedoAvailableFlag; - if (GetZoom() < gMaxZoom && (flags & TracksExistFlag)) + if (ZoomInAvailable() && (flags & TracksExistFlag)) flags |= ZoomInAvailableFlag; - if (GetZoom() > gMinZoom && (flags & TracksExistFlag)) + if (ZoomOutAvailable() && (flags & TracksExistFlag)) flags |= ZoomOutAvailableFlag; if ((flags & LabelTracksExistFlag) && LabelTrack::IsTextClipSupported()) @@ -4882,7 +4882,7 @@ void AudacityProject::ZoomInByFactor( double ZoomFactor ) { // LLL: Handling positioning differently when audio is active if (gAudioIO->IsStreamActive(GetAudioIOToken()) != 0) { - Zoom(mViewInfo.zoom * ZoomFactor); + ZoomBy(ZoomFactor); mTrackPanel->ScrollIntoView(gAudioIO->GetStreamTime()); mTrackPanel->Refresh(false); return; @@ -4915,7 +4915,7 @@ void AudacityProject::ZoomInByFactor( double ZoomFactor ) (mViewInfo.h + mViewInfo.screen - mViewInfo.selectedRegion.t0()) / 2; // Zoom in - Zoom(mViewInfo.zoom *= ZoomFactor); + ZoomBy(ZoomFactor); // Recenter on selCenter TP_ScrollWindow(selCenter - mViewInfo.screen / 2); @@ -4925,7 +4925,7 @@ void AudacityProject::ZoomInByFactor( double ZoomFactor ) double origLeft = mViewInfo.h; double origWidth = mViewInfo.screen; - Zoom(mViewInfo.zoom *= ZoomFactor); + ZoomBy(ZoomFactor); double newh = origLeft + (origWidth - mViewInfo.screen) / 2; @@ -4957,7 +4957,7 @@ void AudacityProject::ZoomOutByFactor( double ZoomFactor ) double origLeft = mViewInfo.h; double origWidth = mViewInfo.screen; - Zoom(mViewInfo.zoom *=ZoomFactor); + ZoomBy(ZoomFactor); double newh = origLeft + (origWidth - mViewInfo.screen) / 2; // newh = (newh > 0) ? newh : 0; @@ -4965,6 +4965,8 @@ void AudacityProject::ZoomOutByFactor( double ZoomFactor ) } +// this is unused: +#if 0 static double OldZooms[2]={ 44100.0/512.0, 4410.0/512.0 }; void AudacityProject::OnZoomToggle() { @@ -4984,11 +4986,12 @@ void AudacityProject::OnZoomToggle() double newh = origLeft + (origWidth - mViewInfo.screen) / 2; TP_ScrollWindow(newh); } +#endif void AudacityProject::OnZoomNormal() { - Zoom(44100.0 / 512.0); + Zoom(ZoomInfo::GetDefaultZoom()); mTrackPanel->Refresh(false); } @@ -5074,7 +5077,7 @@ void AudacityProject::OnZoomSel() // where the selected region may be scrolled off the left of the screen. // I know this isn't right, but until the real rounding or 1-off issue is // found, this will have to work. - Zoom(((mViewInfo.zoom * mViewInfo.screen) - 1) / denom); + Zoom((mViewInfo.GetScreenWidth() - 1) / denom); TP_ScrollWindow(mViewInfo.selectedRegion.t0()); } diff --git a/src/Menus.h b/src/Menus.h index ad0e47cc3..2b5fe80e0 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -259,7 +259,7 @@ void OnSelectAllTracks(); void OnZoomIn(); void OnZoomOut(); -void OnZoomToggle(); +// void OnZoomToggle(); void OnZoomNormal(); void OnZoomFit(); void OnZoomFitV(); diff --git a/src/Project.cpp b/src/Project.cpp index ad61d1e10..51b53a068 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -780,7 +780,7 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, mTimerRecordCanceled(false), mMenuClose(false) , mbInitializingScrollbar(false) - , mViewInfo(0.0, 1.0, (44100.0 / 512.0)) + , mViewInfo(0.0, 1.0, ZoomInfo::GetDefaultZoom()) { // Note that the first field of the status bar is a dummy, and it's width is set // to zero latter in the code. This field is needed for wxWidgets 2.8.12 because @@ -1527,7 +1527,7 @@ void AudacityProject::FixScrollbars() double LastTime = std::max(mTracks->GetEndTime(), mViewInfo.selectedRegion.t1()); - mViewInfo.screen = ((double) panelWidth) / mViewInfo.zoom; + mViewInfo.SetScreenWidth(panelWidth); const double halfScreen = mViewInfo.screen / 2.0; // If we can scroll beyond zero, @@ -1551,9 +1551,9 @@ void AudacityProject::FixScrollbars() rescroll = true; } - mViewInfo.sbarTotal = (wxInt64) (mViewInfo.total * mViewInfo.zoom); - mViewInfo.sbarScreen = (wxInt64) (mViewInfo.screen * mViewInfo.zoom); - mViewInfo.sbarH = (wxInt64) (mViewInfo.h * mViewInfo.zoom); + mViewInfo.sbarTotal = (wxInt64) (mViewInfo.GetTotalWidth()); + mViewInfo.sbarScreen = (wxInt64) (mViewInfo.GetScreenWidth()); + mViewInfo.sbarH = (wxInt64) (mViewInfo.GetBeforeScreenWidth()); int lastv = mViewInfo.vpos; // PRL: Can someone else find a more elegant solution to bug 812, than @@ -1574,7 +1574,7 @@ void AudacityProject::FixScrollbars() bool oldhstate; bool oldvstate; - bool newhstate = mViewInfo.screen < mViewInfo.total; + bool newhstate = !mViewInfo.ZoomedAll(); bool newvstate = panelHeight < totalHeight; #ifdef __WXGTK__ @@ -1585,7 +1585,7 @@ void AudacityProject::FixScrollbars() #else oldhstate = mHsbar->IsEnabled(); oldvstate = mVsbar->IsEnabled(); - mHsbar->Enable(mViewInfo.screen < mViewInfo.total); + mHsbar->Enable(!mViewInfo.ZoomedAll()); mVsbar->Enable(panelHeight < totalHeight); #endif @@ -1595,7 +1595,7 @@ void AudacityProject::FixScrollbars() refresh = true; rescroll = false; } - if (mViewInfo.screen >= mViewInfo.total && mViewInfo.sbarH != 0) { + if (mViewInfo.ZoomedAll() && mViewInfo.sbarH != 0) { mViewInfo.sbarH = 0; refresh = true; @@ -1638,7 +1638,7 @@ void AudacityProject::FixScrollbars() panelHeight / mViewInfo.scrollStep, TRUE); mVsbar->Refresh(); - if (refresh || (rescroll && mViewInfo.screen < mViewInfo.total)) { + if (refresh || (rescroll && !mViewInfo.ZoomedAll())) { mTrackPanel->Refresh(false); } @@ -1821,15 +1821,8 @@ void AudacityProject::OnScroll(wxScrollEvent & WXUNUSED(event)) mViewInfo.sbarH = (wxInt64)(mHsbar->GetThumbPosition() / mViewInfo.sbarScale) - offset; - if (mViewInfo.sbarH != hlast) { - mViewInfo.h = mViewInfo.sbarH / mViewInfo.zoom; - - if (mViewInfo.h > mViewInfo.total - mViewInfo.screen) - mViewInfo.h = mViewInfo.total - mViewInfo.screen; - - if (mViewInfo.h < lowerBound) - mViewInfo.h = lowerBound; - } + if (mViewInfo.sbarH != hlast) + mViewInfo.SetBeforeScreenWidth(mViewInfo.sbarH, lowerBound); if (mScrollBeyondZero) { @@ -4117,12 +4110,14 @@ void AudacityProject::SelectNone() // Utility function called by other zoom methods void AudacityProject::Zoom(double level) { - if (level > gMaxZoom) - level = gMaxZoom; - if (level <= gMinZoom) - level = gMinZoom; + mViewInfo.SetZoom(level); + FixScrollbars(); +} - mViewInfo.zoom = level; +// Utility function called by other zoom methods +void AudacityProject::ZoomBy(double multiplier) +{ + mViewInfo.ZoomBy(multiplier); FixScrollbars(); } diff --git a/src/Project.h b/src/Project.h index c741eac83..bd9b1fa12 100644 --- a/src/Project.h +++ b/src/Project.h @@ -152,7 +152,8 @@ class AUDACITY_DLL_API AudacityProject: public wxFrame, sampleFormat GetDefaultFormat() { return mDefaultFormat; } double GetRate() { return mRate; } - double GetZoom() { return mViewInfo.zoom; } + bool ZoomInAvailable() const { return mViewInfo.ZoomInAvailable(); } + bool ZoomOutAvailable() const { return mViewInfo.ZoomOutAvailable(); } double GetSel0() { return mViewInfo.selectedRegion.t0(); } double GetSel1() { return mViewInfo.selectedRegion.t1(); } @@ -308,6 +309,7 @@ class AUDACITY_DLL_API AudacityProject: public wxFrame, void SelectNone(); void SelectAllIfNone(); void Zoom(double level); + void ZoomBy(double multiplier); void Rewind(bool shift); void SkipEnd(bool shift); void EditByLabel( WaveTrack::EditFunction action, bool bSyncLockedTracks ); diff --git a/src/Screenshot.cpp b/src/Screenshot.cpp index 890416132..94fc80c33 100644 --- a/src/Screenshot.cpp +++ b/src/Screenshot.cpp @@ -661,7 +661,7 @@ void ScreenFrame::TimeZoom(double seconds) { int width, height; mContext.GetProject()->GetClientSize(&width, &height); - mContext.GetProject()->mViewInfo.zoom = (0.75 * width) / seconds; + mContext.GetProject()->mViewInfo.SetZoom((0.75 * width) / seconds); mContext.GetProject()->RedrawProject(); } diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index 4b790b268..5027100f6 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -4568,15 +4568,11 @@ void TrackPanel::DragZoom(wxMouseEvent & event, int trackLeftEdge) double left = PositionToTime(mZoomStart, trackLeftEdge); double right = PositionToTime(mZoomEnd, trackLeftEdge); + double multiplier = mViewInfo->screen / (right - left); if (event.ShiftDown()) - mViewInfo->zoom /= mViewInfo->screen / (right - left); - else - mViewInfo->zoom *= mViewInfo->screen / (right - left); + multiplier = 1.0 / multiplier; - if (mViewInfo->zoom > gMaxZoom) - mViewInfo->zoom = gMaxZoom; - if (mViewInfo->zoom <= gMinZoom) - mViewInfo->zoom = gMinZoom; + mViewInfo->ZoomBy(multiplier); mViewInfo->h = left; } @@ -4588,13 +4584,13 @@ void TrackPanel::DoZoomInOut(wxMouseEvent & event, int trackLeftEdge) { double center_h = PositionToTime(event.m_x, trackLeftEdge); - if (event.RightUp() || event.RightDClick() || event.ShiftDown()) - mViewInfo->zoom = wxMax(mViewInfo->zoom / 2.0, gMinZoom); - else - mViewInfo->zoom = wxMin(mViewInfo->zoom * 2.0, gMaxZoom); + const double multiplier = + (event.RightUp() || event.RightDClick() || event.ShiftDown()) + ? 0.5 : 2.0; + mViewInfo->ZoomBy(multiplier); if (event.MiddleUp() || event.MiddleDClick()) - mViewInfo->zoom = 44100.0 / 512.0; // AS: Reset zoom. + mViewInfo->SetZoom(ZoomInfo::GetDefaultZoom()); // AS: Reset zoom. double new_center_h = PositionToTime(event.m_x, trackLeftEdge); @@ -5301,7 +5297,7 @@ void TrackPanel::UpdateViewIfNoTracks() { // BG: There are no more tracks on screen //BG: Set zoom to normal - mViewInfo->zoom = 44100.0 / 512.0; + mViewInfo->SetZoom(ZoomInfo::GetDefaultZoom()); //STM: Set selection to 0,0 //PRL: and default the rest of the selection information @@ -6322,8 +6318,7 @@ void TrackPanel::HandleWheelRotation(wxMouseEvent & event) center_h = audioEndTime; } - // Constrain maximum as well as minimum zoom. - mViewInfo->zoom = wxMax( gMinZoom, wxMin(mViewInfo->zoom * pow(2.0, steps), gMaxZoom)); + mViewInfo->ZoomBy(pow(2.0, steps)); double new_center_h = PositionToTime(xx, trackLeftEdge); mViewInfo->h += (center_h - new_center_h); @@ -8007,11 +8002,14 @@ void TrackPanel::OnToggle() // Make sure selection edge is in view void TrackPanel::ScrollIntoView(double pos) { + const int screenWidth = rint(mViewInfo->GetScreenWidth()); + int w, h; GetTracksUsableArea( &w, &h ); + // Or should we just set w = screenWidth ? - if( ( pos < mViewInfo->h ) || - ( pos > mViewInfo->h + ( ( w - 1 ) / mViewInfo->zoom ) ) ) + int pixel = mViewInfo->TimeToPosition(pos); + if (pixel < 0 || pixel >= screenWidth) { mListener->TP_ScrollWindow( pos - ( ( w / 2 ) / mViewInfo->zoom ) ); Refresh(false); @@ -8620,7 +8618,7 @@ void TrackPanel::OnTrackClose() if( mTracks->IsEmpty() ) { //BG: Set zoom to normal - mViewInfo->zoom = 44100.0 / 512.0; + mViewInfo->SetZoom(ZoomInfo::GetDefaultZoom()); //STM: Set selection to 0,0 //PRL: and default the rest of the selection information diff --git a/src/ViewInfo.cpp b/src/ViewInfo.cpp index 33b2ad78e..fbd4e8d46 100644 --- a/src/ViewInfo.cpp +++ b/src/ViewInfo.cpp @@ -10,9 +10,16 @@ Paul Licameli #include "ViewInfo.h" +#include + #include "Internat.h" #include "xml/XMLWriter.h" +namespace { +static const double gMaxZoom = 6000000; +static const double gMinZoom = 0.001; +} + ZoomInfo::ZoomInfo(double start, double screenDuration, double pixelsPerSecond) : vpos(0) , h(start) @@ -25,6 +32,46 @@ ZoomInfo::~ZoomInfo() { } +/// Converts a position (mouse X coordinate) to +/// project time, in seconds. Needs the left edge of +/// the track as an additional parameter. +double ZoomInfo::PositionToTime(wxInt64 position, + wxInt64 origin + ) const +{ + return h + (position - origin) / zoom; +} + + +/// STM: Converts a project time to screen x position. +wxInt64 ZoomInfo::TimeToPosition(double projectTime, + wxInt64 origin + ) const +{ + return floor(0.5 + + zoom * (projectTime - h) + origin + ); +} + +bool ZoomInfo::ZoomInAvailable() const +{ + return zoom < gMaxZoom; +} + +bool ZoomInfo::ZoomOutAvailable() const +{ + return zoom > gMinZoom; +} + +void ZoomInfo::SetZoom(double pixelsPerSecond) +{ + zoom = std::max(gMinZoom, std::min(gMaxZoom, pixelsPerSecond)); +} + +void ZoomInfo::ZoomBy(double multiplier) +{ + SetZoom(zoom * multiplier); +} ViewInfo::ViewInfo(double start, double screenDuration, double pixelsPerSecond) : ZoomInfo(start, screenDuration, pixelsPerSecond) @@ -40,6 +87,14 @@ ViewInfo::ViewInfo(double start, double screenDuration, double pixelsPerSecond) { } +void ViewInfo::SetBeforeScreenWidth(wxInt64 width, double lowerBoundTime) +{ + h = + std::max(lowerBoundTime, + std::min(total - screen, + width / zoom)); +} + void ViewInfo::WriteXMLAttributes(XMLWriter &xmlFile) { selectedRegion.WriteXMLAttributes(xmlFile, wxT("sel0"), wxT("sel1")); diff --git a/src/ViewInfo.h b/src/ViewInfo.h index 013006b3d..b9bffac91 100644 --- a/src/ViewInfo.h +++ b/src/ViewInfo.h @@ -13,8 +13,6 @@ #include "SelectedRegion.h" -const double gMaxZoom = 6000000, - gMinZoom = 0.001; class Track; @@ -34,6 +32,52 @@ public: double screen; // screen width in secs double zoom; // pixels per second + +public: + + // do NOT use this once to convert a pixel width to a duration! + // Instead, call twice to convert start and end times, + // and take the difference. + // origin specifies the pixel corresponding to time h + double PositionToTime(wxInt64 position, + wxInt64 origin = 0 + ) const; + + // do NOT use this once to convert a duration to a pixel width! + // Instead, call twice to convert start and end positions, + // and take the difference. + // origin specifies the pixel corresponding to time h + wxInt64 TimeToPosition(double time, + wxInt64 origin = 0 + ) const; + + double OffsetTimeByPixels(double time, wxInt64 offset) const + { + return PositionToTime(offset + TimeToPosition(time)); + } + + bool ZoomInAvailable() const; + bool ZoomOutAvailable() const; + + // Return pixels, but maybe not a whole number + double GetScreenWidth() const + { return screen * zoom; } + + void SetScreenWidth(wxInt64 width) + { screen = width / zoom; } + + static double GetDefaultZoom() + { return 44100.0 / 512.0; } + + // There is NO GetZoom()! + // Use TimeToPosition and PositionToTime and OffsetTimeByPixels! + + // Limits zoom to certain bounds + void SetZoom(double pixelsPerSecond); + + // Limits zoom to certain bounds + // multipliers above 1.0 zoom in, below out + void ZoomBy(double multiplier); }; class AUDACITY_DLL_API ViewInfo @@ -42,6 +86,18 @@ class AUDACITY_DLL_API ViewInfo public: ViewInfo(double start, double screenDuration, double pixelsPerSecond); + double GetBeforeScreenWidth() const + { + return h * zoom; + } + void SetBeforeScreenWidth(wxInt64 width, double lowerBoundTime = 0.0); + + double GetTotalWidth() const + { return total * zoom; } + + bool ZoomedAll() const + { return screen >= total; } + // Current selection SelectedRegion selectedRegion; diff --git a/src/import/ImportLOF.cpp b/src/import/ImportLOF.cpp index 9bd2d999f..fb1d0ad1c 100644 --- a/src/import/ImportLOF.cpp +++ b/src/import/ImportLOF.cpp @@ -70,6 +70,7 @@ *//*******************************************************************/ #include "../Audacity.h" +#include "ImportLOF.h" #include #include @@ -78,7 +79,6 @@ #include #include -#include "ImportLOF.h" #ifdef USE_MIDI #include "ImportMIDI.h" #endif // USE_MIDI @@ -485,8 +485,7 @@ void LOFImportFileHandle::doDuration() if (callDurationFactor) { double longestDuration = mProject->GetTracks()->GetEndTime(); - double realZoomValue = ((longestDuration/durationFactor)*(mProject->GetZoom())); - mProject->Zoom(realZoomValue); + mProject->ZoomBy(longestDuration / durationFactor); callDurationFactor = false; } } diff --git a/src/toolbars/EditToolBar.cpp b/src/toolbars/EditToolBar.cpp index b314388d9..16237ed01 100644 --- a/src/toolbars/EditToolBar.cpp +++ b/src/toolbars/EditToolBar.cpp @@ -32,6 +32,7 @@ #include "../Audacity.h" +#include "EditToolBar.h" // For compilers that support precompilation, includes "wx/wx.h". #include @@ -44,8 +45,6 @@ #include #endif -#include "EditToolBar.h" - #include "../AllThemeResources.h" #include "../AudioIO.h" #include "../ImageManipulation.h" @@ -308,8 +307,8 @@ void EditToolBar::EnableDisableButtons() bool tracks = (!p->GetTracks()->IsEmpty()); - mButtons[ETBZoomInID]->SetEnabled(tracks && (p->GetZoom() < gMaxZoom)); - mButtons[ETBZoomOutID]->SetEnabled(tracks && (p->GetZoom() > gMinZoom) ); + mButtons[ETBZoomInID]->SetEnabled(tracks && (p->ZoomInAvailable())); + mButtons[ETBZoomOutID]->SetEnabled(tracks && (p->ZoomOutAvailable()) ); #if 0 // Disabled for version 1.2.0 since it doesn't work quite right... mButtons[ETBZoomToggleID]->SetEnabled(tracks); diff --git a/src/widgets/AttachableScrollBar.cpp b/src/widgets/AttachableScrollBar.cpp index 241201254..5ffb2fefd 100644 --- a/src/widgets/AttachableScrollBar.cpp +++ b/src/widgets/AttachableScrollBar.cpp @@ -28,9 +28,9 @@ internally, not ints, allowing for (external) control of zooming. *//*******************************************************************/ #include "../Audacity.h" +#include "AttachableScrollBar.h" #include -#include "AttachableScrollBar.h" #include "../ViewInfo.h" @@ -58,9 +58,9 @@ void AttachableScrollBar::SetScrollBarFromViewInfo() { ViewInfo & mViewInfo = *mpViewInfo; - mViewInfo.sbarTotal = (int) (mViewInfo.total * mViewInfo.zoom); - mViewInfo.sbarScreen = (int) (mViewInfo.screen * mViewInfo.zoom); - mViewInfo.sbarH = (int) (mViewInfo.h * mViewInfo.zoom); + mViewInfo.sbarTotal = (int) (mViewInfo.GetTotalWidth()); + mViewInfo.sbarScreen = (int) (mViewInfo.GetScreenWidth()); + mViewInfo.sbarH = (int) (mViewInfo.GetBeforeScreenWidth()); SetScrollbar(mViewInfo.sbarH, mViewInfo.sbarScreen, mViewInfo.sbarTotal, mViewInfo.sbarScreen, TRUE); @@ -75,14 +75,8 @@ void AttachableScrollBar::SetViewInfoFromScrollBar() mViewInfo.sbarH = GetThumbPosition(); - if (mViewInfo.sbarH != hlast) { - mViewInfo.h = mViewInfo.sbarH / mViewInfo.zoom; - - if (mViewInfo.h > mViewInfo.total - mViewInfo.screen) - mViewInfo.h = mViewInfo.total - mViewInfo.screen; - if (mViewInfo.h < 0.0) - mViewInfo.h = 0.0; - } + if (mViewInfo.sbarH != hlast) + mViewInfo.SetBeforeScreenWidth(mViewInfo.sbarH); } // Used to associated a ViewInfo structure with a scrollbar.