1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-16 08:34:10 +02:00

Fix off-by-one or -few bugs in drawing and mousing; explained some magic numbers

This commit is contained in:
Paul Licameli 2015-08-21 22:49:06 -04:00
commit 9760f8aac2
15 changed files with 289 additions and 194 deletions

View File

@ -1387,7 +1387,7 @@ bool LabelTrack::HandleGlyphDragRelease(const wxMouseEvent & evt,
//just reset its value and redraw. //just reset its value and redraw.
// LL: Constrain to inside track rectangle for now. Should be changed // LL: Constrain to inside track rectangle for now. Should be changed
// to allow scrolling while dragging labels // to allow scrolling while dragging labels
int x = Constrain( evt.m_x + mxMouseDisplacement - r.x, 0, r.width - 2); int x = Constrain( evt.m_x + mxMouseDisplacement - r.x, 0, r.width);
// If exactly one edge is selected we allow swapping // If exactly one edge is selected we allow swapping
bool bAllowSwapping = (mMouseOverLabelLeft >=0 ) ^ ( mMouseOverLabelRight >= 0); bool bAllowSwapping = (mMouseOverLabelLeft >=0 ) ^ ( mMouseOverLabelRight >= 0);

View File

@ -4824,6 +4824,11 @@ void AudacityProject::OnZoomIn()
ZoomInByFactor( 2.0 ); ZoomInByFactor( 2.0 );
} }
double AudacityProject::GetScreenEndTime() const
{
return mTrackPanel->GetScreenEndTime();
}
void AudacityProject::ZoomInByFactor( double ZoomFactor ) void AudacityProject::ZoomInByFactor( double ZoomFactor )
{ {
// LLL: Handling positioning differently when audio is active // LLL: Handling positioning differently when audio is active
@ -4838,13 +4843,16 @@ void AudacityProject::ZoomInByFactor( double ZoomFactor )
// when there's a selection that's currently at least // when there's a selection that's currently at least
// partially on-screen // partially on-screen
const double endTime = GetScreenEndTime();
const double duration = endTime - mViewInfo.h;
bool selectionIsOnscreen = bool selectionIsOnscreen =
(mViewInfo.selectedRegion.t0() < mViewInfo.h + mViewInfo.screen) && (mViewInfo.selectedRegion.t0() < endTime) &&
(mViewInfo.selectedRegion.t1() >= mViewInfo.h); (mViewInfo.selectedRegion.t1() >= mViewInfo.h);
bool selectionFillsScreen = bool selectionFillsScreen =
(mViewInfo.selectedRegion.t0() < mViewInfo.h) && (mViewInfo.selectedRegion.t0() < mViewInfo.h) &&
(mViewInfo.selectedRegion.t1() > mViewInfo.h + mViewInfo.screen); (mViewInfo.selectedRegion.t1() > endTime);
if (selectionIsOnscreen && !selectionFillsScreen) { if (selectionIsOnscreen && !selectionFillsScreen) {
// Start with the center of the selection // Start with the center of the selection
@ -4856,24 +4864,26 @@ void AudacityProject::ZoomInByFactor( double ZoomFactor )
if (selCenter < mViewInfo.h) if (selCenter < mViewInfo.h)
selCenter = mViewInfo.h + selCenter = mViewInfo.h +
(mViewInfo.selectedRegion.t1() - mViewInfo.h) / 2; (mViewInfo.selectedRegion.t1() - mViewInfo.h) / 2;
if (selCenter > mViewInfo.h + mViewInfo.screen) if (selCenter > endTime)
selCenter = mViewInfo.h + mViewInfo.screen - selCenter = endTime -
(mViewInfo.h + mViewInfo.screen - mViewInfo.selectedRegion.t0()) / 2; (endTime - mViewInfo.selectedRegion.t0()) / 2;
// Zoom in // Zoom in
ZoomBy(ZoomFactor); ZoomBy(ZoomFactor);
const double newDuration = GetScreenEndTime() - mViewInfo.h;
// Recenter on selCenter // Recenter on selCenter
TP_ScrollWindow(selCenter - mViewInfo.screen / 2); TP_ScrollWindow(selCenter - newDuration / 2);
return; return;
} }
double origLeft = mViewInfo.h; double origLeft = mViewInfo.h;
double origWidth = mViewInfo.screen; double origWidth = duration;
ZoomBy(ZoomFactor); ZoomBy(ZoomFactor);
double newh = origLeft + (origWidth - mViewInfo.screen) / 2; const double newDuration = GetScreenEndTime() - mViewInfo.h;
double newh = origLeft + (origWidth - newDuration) / 2;
// MM: Commented this out because it was confusing users // MM: Commented this out because it was confusing users
/* /*
@ -4900,12 +4910,13 @@ void AudacityProject::OnZoomOut()
void AudacityProject::ZoomOutByFactor( double ZoomFactor ) void AudacityProject::ZoomOutByFactor( double ZoomFactor )
{ {
//Zoom() may change these, so record original values: //Zoom() may change these, so record original values:
double origLeft = mViewInfo.h; const double origLeft = mViewInfo.h;
double origWidth = mViewInfo.screen; const double origWidth = GetScreenEndTime() - origLeft;
ZoomBy(ZoomFactor); ZoomBy(ZoomFactor);
const double newWidth = GetScreenEndTime() - mViewInfo.h;
double newh = origLeft + (origWidth - mViewInfo.screen) / 2; const double newh = origLeft + (origWidth - newWidth) / 2;
// newh = (newh > 0) ? newh : 0; // newh = (newh > 0) ? newh : 0;
TP_ScrollWindow(newh); TP_ScrollWindow(newh);
@ -4952,8 +4963,8 @@ void AudacityProject::OnZoomFit()
if (len <= 0.0) if (len <= 0.0)
return; return;
int w, h; int w;
mTrackPanel->GetTracksUsableArea(&w, &h); mTrackPanel->GetTracksUsableArea(&w, NULL);
w -= 10; w -= 10;
Zoom(w / len); Zoom(w / len);
@ -4962,9 +4973,9 @@ void AudacityProject::OnZoomFit()
void AudacityProject::DoZoomFitV() void AudacityProject::DoZoomFitV()
{ {
int width, height, count; int height, count;
mTrackPanel->GetTracksUsableArea(&width, &height); mTrackPanel->GetTracksUsableArea(NULL, &height);
height -= 28; height -= 28;
@ -5023,16 +5034,19 @@ void AudacityProject::OnZoomSel()
// where the selected region may be scrolled off the left of the screen. // 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 // I know this isn't right, but until the real rounding or 1-off issue is
// found, this will have to work. // found, this will have to work.
Zoom((mViewInfo.GetScreenWidth() - 1) / denom); // PRL: Did I fix this? I am not sure, so I leave the hack in place.
int width;
mTrackPanel->GetTracksUsableArea(&width, NULL);
Zoom((width - 1) / denom);
TP_ScrollWindow(mViewInfo.selectedRegion.t0()); TP_ScrollWindow(mViewInfo.selectedRegion.t0());
} }
void AudacityProject::OnGoSelStart() void AudacityProject::OnGoSelStart()
{ {
if (mViewInfo.selectedRegion.isPoint()) if (mViewInfo.selectedRegion.isPoint())
return; return;
TP_ScrollWindow(mViewInfo.selectedRegion.t0() - (mViewInfo.screen / 2)); TP_ScrollWindow(mViewInfo.selectedRegion.t0() - ((GetScreenEndTime() - mViewInfo.h) / 2));
} }
void AudacityProject::OnGoSelEnd() void AudacityProject::OnGoSelEnd()
@ -5040,7 +5054,7 @@ void AudacityProject::OnGoSelEnd()
if (mViewInfo.selectedRegion.isPoint()) if (mViewInfo.selectedRegion.isPoint())
return; return;
TP_ScrollWindow(mViewInfo.selectedRegion.t1() - (mViewInfo.screen / 2)); TP_ScrollWindow(mViewInfo.selectedRegion.t1() - ((GetScreenEndTime() - mViewInfo.h) / 2));
} }
void AudacityProject::OnShowClipping() void AudacityProject::OnShowClipping()

View File

@ -241,10 +241,14 @@ public:
case nstErb: case nstErb:
case nstUndertone: case nstUndertone:
return Iterator return Iterator
(mType, (mValue1 - mValue0) / nPositions, mValue0, mUnit); (mType,
nPositions == 1 ? 0 : (mValue1 - mValue0) / (nPositions - 1),
mValue0, mUnit);
case nstLogarithmic: case nstLogarithmic:
return Iterator return Iterator
(mType, exp((mValue1 - mValue0) / nPositions), exp(mValue0), mUnit); (mType,
nPositions == 1 ? 1 : exp((mValue1 - mValue0) / (nPositions - 1)),
exp(mValue0), mUnit);
} }
} }

View File

@ -80,7 +80,7 @@ bool AudacityPrintout::OnPrintPage(int WXUNUSED(page))
artist.SetBackgroundBrushes(*wxWHITE_BRUSH, *wxWHITE_BRUSH, artist.SetBackgroundBrushes(*wxWHITE_BRUSH, *wxWHITE_BRUSH,
*wxWHITE_PEN, *wxWHITE_PEN); *wxWHITE_PEN, *wxWHITE_PEN);
const double screenDuration = mTracks->GetEndTime(); const double screenDuration = mTracks->GetEndTime();
ZoomInfo zoomInfo(0.0, screenDuration, width / screenDuration); ZoomInfo zoomInfo(0.0, width / screenDuration);
int y = rulerPageHeight; int y = rulerPageHeight;
TrackListIterator iter(mTracks); TrackListIterator iter(mTracks);

View File

@ -1472,9 +1472,10 @@ void AudacityProject::OnScrollRightButton(wxScrollEvent & event)
double AudacityProject::ScrollingLowerBoundTime() const double AudacityProject::ScrollingLowerBoundTime() const
{ {
return mScrollBeyondZero if (!mScrollBeyondZero)
? std::min(mTracks->GetStartTime(), -mViewInfo.screen / 2.0) return 0;
: 0; const double screen = mTrackPanel->GetScreenEndTime() - mViewInfo.h;
return std::min(mTracks->GetStartTime(), -screen / 2.0);
} }
wxInt64 AudacityProject::PixelWidthBeforeTime(double scrollto) const wxInt64 AudacityProject::PixelWidthBeforeTime(double scrollto) const
@ -1556,8 +1557,8 @@ void AudacityProject::FixScrollbars()
double LastTime = double LastTime =
std::max(mTracks->GetEndTime(), mViewInfo.selectedRegion.t1()); std::max(mTracks->GetEndTime(), mViewInfo.selectedRegion.t1());
mViewInfo.SetScreenWidth(panelWidth); const double screen = GetScreenEndTime() - mViewInfo.h;
const double halfScreen = mViewInfo.screen / 2.0; const double halfScreen = screen / 2.0;
// If we can scroll beyond zero, // If we can scroll beyond zero,
// Add 1/2 of a screen of blank space to the end // Add 1/2 of a screen of blank space to the end
@ -1567,13 +1568,13 @@ void AudacityProject::FixScrollbars()
// May add even more to the end, so that you can always scroll the starting time to zero. // May add even more to the end, so that you can always scroll the starting time to zero.
const double lowerBound = ScrollingLowerBoundTime(); const double lowerBound = ScrollingLowerBoundTime();
const double additional = mScrollBeyondZero const double additional = mScrollBeyondZero
? -lowerBound + std::max(halfScreen, mViewInfo.screen - LastTime) ? -lowerBound + std::max(halfScreen, screen - LastTime)
: mViewInfo.screen / 4.0; : screen / 4.0;
mViewInfo.total = LastTime + additional; mViewInfo.total = LastTime + additional;
// Don't remove time from total that's still on the screen // Don't remove time from total that's still on the screen
mViewInfo.total = std::max(mViewInfo.total, mViewInfo.h + mViewInfo.screen); mViewInfo.total = std::max(mViewInfo.total, mViewInfo.h + screen);
if (mViewInfo.h < lowerBound) { if (mViewInfo.h < lowerBound) {
mViewInfo.h = lowerBound; mViewInfo.h = lowerBound;
@ -1581,7 +1582,7 @@ void AudacityProject::FixScrollbars()
} }
mViewInfo.sbarTotal = (wxInt64) (mViewInfo.GetTotalWidth()); mViewInfo.sbarTotal = (wxInt64) (mViewInfo.GetTotalWidth());
mViewInfo.sbarScreen = (wxInt64) (mViewInfo.GetScreenWidth()); mViewInfo.sbarScreen = (wxInt64)(panelWidth);
mViewInfo.sbarH = (wxInt64) (mViewInfo.GetBeforeScreenWidth()); mViewInfo.sbarH = (wxInt64) (mViewInfo.GetBeforeScreenWidth());
int lastv = mViewInfo.vpos; int lastv = mViewInfo.vpos;
@ -1603,7 +1604,7 @@ void AudacityProject::FixScrollbars()
bool oldhstate; bool oldhstate;
bool oldvstate; bool oldvstate;
bool newhstate = !mViewInfo.ZoomedAll(); bool newhstate = (GetScreenEndTime() - mViewInfo.h) < mViewInfo.total;
bool newvstate = panelHeight < totalHeight; bool newvstate = panelHeight < totalHeight;
#ifdef __WXGTK__ #ifdef __WXGTK__
@ -1614,7 +1615,7 @@ void AudacityProject::FixScrollbars()
#else #else
oldhstate = mHsbar->IsEnabled(); oldhstate = mHsbar->IsEnabled();
oldvstate = mVsbar->IsEnabled(); oldvstate = mVsbar->IsEnabled();
mHsbar->Enable(!mViewInfo.ZoomedAll()); mHsbar->Enable(newhstate);
mVsbar->Enable(panelHeight < totalHeight); mVsbar->Enable(panelHeight < totalHeight);
#endif #endif
@ -1624,7 +1625,7 @@ void AudacityProject::FixScrollbars()
refresh = true; refresh = true;
rescroll = false; rescroll = false;
} }
if (mViewInfo.ZoomedAll() && mViewInfo.sbarH != 0) { if (!newhstate && mViewInfo.sbarH != 0) {
mViewInfo.sbarH = 0; mViewInfo.sbarH = 0;
refresh = true; refresh = true;
@ -1666,7 +1667,8 @@ void AudacityProject::FixScrollbars()
panelHeight / mViewInfo.scrollStep, TRUE); panelHeight / mViewInfo.scrollStep, TRUE);
mVsbar->Refresh(); mVsbar->Refresh();
if (refresh || (rescroll && !mViewInfo.ZoomedAll())) { if (refresh || (rescroll &&
(GetScreenEndTime() - mViewInfo.h) < mViewInfo.total)) {
mTrackPanel->Refresh(false); mTrackPanel->Refresh(false);
} }
@ -1849,8 +1851,12 @@ void AudacityProject::OnScroll(wxScrollEvent & WXUNUSED(event))
mViewInfo.sbarH = mViewInfo.sbarH =
(wxInt64)(mHsbar->GetThumbPosition() / mViewInfo.sbarScale) - offset; (wxInt64)(mHsbar->GetThumbPosition() / mViewInfo.sbarScale) - offset;
if (mViewInfo.sbarH != hlast) if (mViewInfo.sbarH != hlast) {
mViewInfo.SetBeforeScreenWidth(mViewInfo.sbarH, lowerBound); int width;
mTrackPanel->GetTracksUsableArea(&width, NULL);
mViewInfo.SetBeforeScreenWidth(mViewInfo.sbarH, width, lowerBound);
}
if (mScrollBeyondZero) { if (mScrollBeyondZero) {
enum { SCROLL_PIXEL_TOLERANCE = 10 }; enum { SCROLL_PIXEL_TOLERANCE = 10 };

View File

@ -301,6 +301,7 @@ class AUDACITY_DLL_API AudacityProject: public wxFrame,
void HandleResize(); void HandleResize();
void UpdateLayout(); void UpdateLayout();
double GetScreenEndTime() const;
void ZoomInByFactor( double ZoomFactor ); void ZoomInByFactor( double ZoomFactor );
void ZoomOutByFactor( double ZoomFactor ); void ZoomOutByFactor( double ZoomFactor );

View File

@ -503,7 +503,7 @@ void TrackArtist::DrawVRuler(Track *t, wxDC * dc, wxRect & rect)
// But give it a beveled area // But give it a beveled area
if (kind == Track::Label) { if (kind == Track::Label) {
wxRect bev = rect; wxRect bev = rect;
bev.Inflate(-1, -1); bev.Inflate(-1, 0);
bev.width += 1; bev.width += 1;
AColor::BevelTrackInfo(*dc, true, bev); AColor::BevelTrackInfo(*dc, true, bev);
@ -513,7 +513,7 @@ void TrackArtist::DrawVRuler(Track *t, wxDC * dc, wxRect & rect)
// Time tracks // Time tracks
if (kind == Track::Time) { if (kind == Track::Time) {
wxRect bev = rect; wxRect bev = rect;
bev.Inflate(-1, -1); bev.Inflate(-1, 0);
bev.width += 1; bev.width += 1;
AColor::BevelTrackInfo(*dc, true, bev); AColor::BevelTrackInfo(*dc, true, bev);
@ -537,7 +537,7 @@ void TrackArtist::DrawVRuler(Track *t, wxDC * dc, wxRect & rect)
// The ruler needs a bevelled surround. // The ruler needs a bevelled surround.
if (kind == Track::Wave) { if (kind == Track::Wave) {
wxRect bev = rect; wxRect bev = rect;
bev.Inflate(-1, -1); bev.Inflate(-1, 0);
bev.width += 1; bev.width += 1;
AColor::BevelTrackInfo(*dc, true, bev); AColor::BevelTrackInfo(*dc, true, bev);
@ -566,13 +566,11 @@ void TrackArtist::DrawVRuler(Track *t, wxDC * dc, wxRect & rect)
dc->SetBrush(*wxWHITE_BRUSH); dc->SetBrush(*wxWHITE_BRUSH);
wxRect bev = rect; wxRect bev = rect;
bev.x++; bev.x++;
bev.y++;
bev.width--; bev.width--;
bev.height--;
dc->DrawRectangle(bev); dc->DrawRectangle(bev);
rect.y += 2; rect.y += 1;
rect.height -= 2; rect.height -= 1;
//int bottom = GetBottom((NoteTrack *) t, rect); //int bottom = GetBottom((NoteTrack *) t, rect);
NoteTrack *track = (NoteTrack *) t; NoteTrack *track = (NoteTrack *) t;
@ -671,7 +669,7 @@ void TrackArtist::UpdateVRuler(Track *t, wxRect & rect)
min = tt->GetRangeLower() * 100.0; min = tt->GetRangeLower() * 100.0;
max = tt->GetRangeUpper() * 100.0; max = tt->GetRangeUpper() * 100.0;
vruler->SetBounds(rect.x, rect.y+1, rect.x + rect.width, rect.y + rect.height-1); vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height-1);
vruler->SetOrientation(wxVERTICAL); vruler->SetOrientation(wxVERTICAL);
vruler->SetRange(max, min); vruler->SetRange(max, min);
vruler->SetFormat((tt->GetDisplayLog()) ? Ruler::RealLogFormat : Ruler::RealFormat); vruler->SetFormat((tt->GetDisplayLog()) ? Ruler::RealLogFormat : Ruler::RealFormat);
@ -720,7 +718,7 @@ void TrackArtist::UpdateVRuler(Track *t, wxRect & rect)
wt->SetDisplayBounds(min, max); wt->SetDisplayBounds(min, max);
} }
vruler->SetBounds(rect.x, rect.y + 1, rect.x + rect.width, rect.y + rect.height - 1); vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
vruler->SetOrientation(wxVERTICAL); vruler->SetOrientation(wxVERTICAL);
vruler->SetRange(max, min); vruler->SetRange(max, min);
vruler->SetFormat(Ruler::RealFormat); vruler->SetFormat(Ruler::RealFormat);
@ -782,7 +780,7 @@ void TrackArtist::UpdateVRuler(Track *t, wxRect & rect)
botval = -((1 - min) * dBRange); botval = -((1 - min) * dBRange);
} }
vruler->SetBounds(rect.x, rect.y + top + 1, rect.x + rect.width, rect.y + bot - 1); vruler->SetBounds(rect.x, rect.y + top, rect.x + rect.width, rect.y + bot - 1);
vruler->SetOrientation(wxVERTICAL); vruler->SetOrientation(wxVERTICAL);
vruler->SetRange(topval, botval); vruler->SetRange(topval, botval);
} }
@ -815,7 +813,7 @@ void TrackArtist::UpdateVRuler(Track *t, wxRect & rect)
we will use Hz if maxFreq is < 2000, otherwise we represent kHz, we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
and append to the numbers a "k" and append to the numbers a "k"
*/ */
vruler->SetBounds(rect.x, rect.y + 1, rect.x + rect.width, rect.y + rect.height - 1); vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
vruler->SetOrientation(wxVERTICAL); vruler->SetOrientation(wxVERTICAL);
vruler->SetFormat(Ruler::RealFormat); vruler->SetFormat(Ruler::RealFormat);
vruler->SetLabelEdges(true); vruler->SetLabelEdges(true);
@ -853,7 +851,7 @@ void TrackArtist::UpdateVRuler(Track *t, wxRect & rect)
we will use Hz if maxFreq is < 2000, otherwise we represent kHz, we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
and append to the numbers a "k" and append to the numbers a "k"
*/ */
vruler->SetBounds(rect.x, rect.y + 1, rect.x + rect.width, rect.y + rect.height - 1); vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
vruler->SetOrientation(wxVERTICAL); vruler->SetOrientation(wxVERTICAL);
vruler->SetFormat(Ruler::IntFormat); vruler->SetFormat(Ruler::IntFormat);
vruler->SetLabelEdges(true); vruler->SetLabelEdges(true);
@ -873,7 +871,7 @@ void TrackArtist::UpdateVRuler(Track *t, wxRect & rect)
// The note track isn't drawing a ruler at all! // The note track isn't drawing a ruler at all!
// But it needs to! // But it needs to!
else if (t->GetKind() == Track::Note) { else if (t->GetKind() == Track::Note) {
vruler->SetBounds(rect.x, rect.y+1, rect.x + 1, rect.y + rect.height-1); vruler->SetBounds(rect.x, rect.y, rect.x + 1, rect.y + rect.height-1);
vruler->SetOrientation(wxVERTICAL); vruler->SetOrientation(wxVERTICAL);
} }
#endif // USE_MIDI #endif // USE_MIDI
@ -946,7 +944,10 @@ float ValueOfPixel(int yy, int height, bool offset,
bool dB, double dBRange, float zoomMin, float zoomMax) bool dB, double dBRange, float zoomMin, float zoomMax)
{ {
wxASSERT(height > 0); wxASSERT(height > 0);
float v = zoomMax - (yy / (float)height) * (zoomMax - zoomMin); // Map 0 to max and height - 1 (not height) to min
float v =
height == 1 ? (zoomMin + zoomMax) / 2 :
zoomMax - (yy / (float)(height - 1)) * (zoomMax - zoomMin);
if (offset) { if (offset) {
if (v > 0.0) if (v > 0.0)
v += .5; v += .5;
@ -1662,7 +1663,7 @@ void FindWavePortions
// the fisheye. // the fisheye.
ZoomInfo::Intervals intervals; ZoomInfo::Intervals intervals;
zoomInfo.FindIntervals(params.rate, intervals, rect.x); zoomInfo.FindIntervals(params.rate, intervals, rect.width, rect.x);
ZoomInfo::Intervals::const_iterator it = intervals.begin(), end = intervals.end(), prev; ZoomInfo::Intervals::const_iterator it = intervals.begin(), end = intervals.end(), prev;
wxASSERT(it != end && it->position == rect.x); wxASSERT(it != end && it->position == rect.x);
const int rightmost = rect.x + rect.width; const int rightmost = rect.x + rect.width;
@ -3282,12 +3283,12 @@ void TrackArtist::DrawBackgroundWithSelection(wxDC *dc, const wxRect &rect,
dc->SetBrush(unselBrush); dc->SetBrush(unselBrush);
dc->DrawRectangle(before); dc->DrawRectangle(before);
within.x = before.GetRight(); within.x = 1 + before.GetRight();
} }
within.width = rect.x + int(zoomInfo.TimeToPosition(sel1) + 2) - within.x; within.width = rect.x + int(zoomInfo.TimeToPosition(sel1) + 2) - within.x;
if (within.GetRight() > rect.GetRight()) { if (within.GetRight() > rect.GetRight()) {
within.width = rect.GetRight() - within.x; within.width = 1 + rect.GetRight() - within.x;
} }
if (within.width > 0) { if (within.width > 0) {
@ -3302,14 +3303,14 @@ void TrackArtist::DrawBackgroundWithSelection(wxDC *dc, const wxRect &rect,
DrawSyncLockTiles(dc, within); DrawSyncLockTiles(dc, within);
} }
after.x = within.GetRight(); after.x = 1 + within.GetRight();
} }
else { else {
// `within` not drawn; start where it would have gone // `within` not drawn; start where it would have gone
after.x = within.x; after.x = within.x;
} }
after.width = rect.GetRight() - after.x; after.width = 1 + rect.GetRight() - after.x;
if (after.width > 0) { if (after.width > 0) {
dc->SetBrush(unselBrush); dc->SetBrush(unselBrush);
dc->DrawRectangle(after); dc->DrawRectangle(after);

View File

@ -206,9 +206,70 @@ is time to refresh some aspect of the screen.
DEFINE_EVENT_TYPE(EVT_TRACK_PANEL_TIMER) DEFINE_EVENT_TYPE(EVT_TRACK_PANEL_TIMER)
/*
This is a diagram of TrackPanel's division of one (non-stereo) track rectangle.
Total height equals Track::GetHeight()'s value. Total width is the wxWindow's width.
Each charater that is not . represents one pixel.
Inset space of this track, and top inset of the next track, are used to draw the focus highlight.
Top inset of the right channel of a stereo track, and bottom shadow line of the
left channel, are used for the channel separator.
TrackInfo::GetTrackInfoWidth() == GetVRulerOffset()
counts columns from the left edge up to and including controls, and is a constant.
GetVRulerWidth() is variable -- all tracks have the same ruler width at any time,
but that width may be adjusted when tracks change their vertical scales.
GetLabelWidth() counts columns up to and including the VRuler.
GetLeftOffset() is yet one more -- it counts the "one pixel" column.
FindTrack() for label returns a rectangle up to and including the One Pixel column,
but OMITS left and top insets
FindTrack() for !label returns a rectangle with x == GetLeftOffset(), and INCLUDES
right and top insets
+--------------- ... ------ ... --------------------- ... ... -------------+
| Top Inset |
| |
| +------------ ... ------ ... --------------------- ... ... ----------+ |
| L|+-Border---- ... ------ ... --------------------- ... ... -Border-+ |R |
| e||+---------- ... -++--- ... -+++----------------- ... ... -------+| |i |
| f|B| || ||| |BS|g |
| t|o| Controls || V |O| The good stuff |oh|h |
| |r| || R |n| |ra|t |
| I|d| || u |e| |dd| |
| n|e| || l | | |eo|I |
| s|r| || e |P| |rw|n |
| e||| || r |i| ||||s |
| t||| || |x| ||||e |
| ||| || |e| ||||t |
| ||| || |l| |||| |
| ||| || ||| |||| |
. ... .. ... .... .
. ... .. ... .... .
. ... .. ... .... .
| ||| || ||| |||| |
| ||+---------- -++-- ... -+++----------------- ... ... -------+||| |
| |+-Border---- ... ----- ... --------------------- ... ... -Border-+|| |
| | Shadow---- ... ----- ... --------------------- ... ... --Shadow-+| |
*/
enum { enum {
kLeftInset = 4, kLeftInset = 4,
kRightInset = kLeftInset,
kTopInset = 4, kTopInset = 4,
kShadowThickness = 1,
kBorderThickness = 1,
kTopMargin = kTopInset + kBorderThickness,
kBottomMargin = kShadowThickness + kBorderThickness,
kLeftMargin = kLeftInset + kBorderThickness,
kRightMargin = kRightInset + kShadowThickness + kBorderThickness,
kTimerInterval = 50, // milliseconds kTimerInterval = 50, // milliseconds
kOneSecondCountdown = 1000 / kTimerInterval, kOneSecondCountdown = 1000 / kTimerInterval,
}; };
@ -231,15 +292,9 @@ template < class A, class B, class DIST > bool within(A a, B b, DIST d)
} }
template < class LOW, class MID, class HIGH > template < class LOW, class MID, class HIGH >
bool between_inclusive(LOW l, MID m, HIGH h) bool between_incexc(LOW l, MID m, HIGH h)
{ {
return (m >= l && m <= h); return (m >= l && m < h);
}
template < class LOW, class MID, class HIGH >
bool between_exclusive(LOW l, MID m, HIGH h)
{
return (m > l && m < h);
} }
template < class CLIPPEE, class CLIPVAL > template < class CLIPPEE, class CLIPVAL >
@ -536,7 +591,7 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id,
BuildMenus(); BuildMenus();
mTrackArtist = new TrackArtist(); mTrackArtist = new TrackArtist();
mTrackArtist->SetInset(1, kTopInset + 1, kLeftInset + 2, 2); mTrackArtist->SetInset(1, kTopMargin, kRightMargin, kBottomMargin);
mCapturedTrack = NULL; mCapturedTrack = NULL;
mPopupMenuTarget = NULL; mPopupMenuTarget = NULL;
@ -1013,10 +1068,11 @@ void TrackPanel::SelectTrackLength(Track *t)
void TrackPanel::GetTracksUsableArea(int *width, int *height) const void TrackPanel::GetTracksUsableArea(int *width, int *height) const
{ {
GetSize(width, height); GetSize(width, height);
*width -= GetLabelWidth(); if (width) {
// AS: MAGIC NUMBER: What does 2 represent? *width -= GetLeftOffset();
*width -= 2 + kLeftInset; *width -= kRightMargin;
*width = std::max(0, *width); *width = std::max(0, *width);
}
} }
/// Gets the pointer to the AudacityProject that /// Gets the pointer to the AudacityProject that
@ -1188,14 +1244,22 @@ void TrackPanel::DrawQuickPlayIndicator(wxDC & dc, double pos)
// Draw the new indicator in its new location // Draw the new indicator in its new location
AColor::Line(dc, AColor::Line(dc,
pos, pos,
y + kTopInset + 1, y + kTopMargin,
pos, pos,
y + t->GetHeight() - 3 ); // Minus one more because AColor::Line includes both endpoints
y + t->GetHeight() - kBottomMargin - 1 );
} }
mOldQPIndicatorPos = pos; mOldQPIndicatorPos = pos;
} }
} }
double TrackPanel::GetScreenEndTime() const
{
int width;
GetTracksUsableArea(&width, NULL);
return mViewInfo->PositionToTime(width, true);
}
void TrackPanel::TimerUpdateIndicator() void TrackPanel::TimerUpdateIndicator()
{ {
double pos = 0.0; double pos = 0.0;
@ -1209,7 +1273,8 @@ void TrackPanel::TimerUpdateIndicator()
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL #ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
if (mSmoothScrollingScrub) { if (mSmoothScrollingScrub) {
// Pan the view, so that we center the play indicator. // Pan the view, so that we center the play indicator.
mViewInfo->h = pos - mViewInfo->screen / 2.0; const double duration = GetScreenEndTime() - mViewInfo->h;
mViewInfo->h = pos - duration / 2.0;
if (!mScrollBeyondZero) if (!mScrollBeyondZero)
// Can't scroll too far left // Can't scroll too far left
mViewInfo->h = std::max(0.0, mViewInfo->h); mViewInfo->h = std::max(0.0, mViewInfo->h);
@ -1218,9 +1283,9 @@ void TrackPanel::TimerUpdateIndicator()
#endif #endif
AudacityProject *p = GetProject(); AudacityProject *p = GetProject();
const bool const bool
onScreen = between_inclusive(mViewInfo->h, onScreen = between_incexc(mViewInfo->h,
pos, pos,
mViewInfo->h + mViewInfo->screen); GetScreenEndTime());
// This displays the audio time, too... // This displays the audio time, too...
DisplaySelection(); DisplaySelection();
@ -1263,10 +1328,12 @@ void TrackPanel::UndrawIndicator(wxDC & dc)
// Erase the old indicator. // Erase the old indicator.
if (mLastIndicatorX != -1) if (mLastIndicatorX != -1)
{ {
int width;
GetTracksUsableArea(&width, NULL);
const bool const bool
onScreen = between_inclusive(GetLeftOffset(), onScreen = between_incexc(GetLeftOffset(),
mLastIndicatorX, mLastIndicatorX,
GetLeftOffset() + mViewInfo->GetScreenWidth()); GetLeftOffset() + width);
if (onScreen) if (onScreen)
{ {
// LL: Keep from trying to blit outsize of the source DC. This results in a crash on // LL: Keep from trying to blit outsize of the source DC. This results in a crash on
@ -1300,10 +1367,9 @@ void TrackPanel::DoDrawIndicator(wxDC & dc)
// Ensure that we don't draw through the TrackInfo or vertical ruler. // Ensure that we don't draw through the TrackInfo or vertical ruler.
wxRect clip = GetRect(); wxRect clip = GetRect();
int leftCutoff = clip.x + GetLabelWidth(); int leftCutoff = clip.x + GetLeftOffset();
int rightInset = kLeftInset + 2; // See the call to SetInset int rightCutoff = clip.x + clip.width - kRightMargin;
int rightCutoff = clip.x + clip.width - rightInset; if (!between_incexc(leftCutoff, mLastIndicatorX, rightCutoff))
if (!between_inclusive(leftCutoff, mLastIndicatorX, rightCutoff))
{ {
return; return;
} }
@ -1324,9 +1390,10 @@ void TrackPanel::DoDrawIndicator(wxDC & dc)
// Draw the new indicator in its new location // Draw the new indicator in its new location
AColor::Line(dc, AColor::Line(dc,
mLastIndicatorX, mLastIndicatorX,
y + kTopInset + 1, y + kTopMargin,
mLastIndicatorX, mLastIndicatorX,
y + t->GetHeight() - 3 ); // Minus one more because AColor::Line includes both endpoints
y + t->GetHeight() - kBottomMargin - 1);
} }
} }
@ -1366,9 +1433,11 @@ void TrackPanel::UndrawCursor(wxDC & dc)
if( mLastCursorX != -1 ) if( mLastCursorX != -1 )
{ {
onScreen = between_inclusive(GetLeftOffset(), int width;
mLastCursorX, GetTracksUsableArea(&width, NULL);
GetLeftOffset() + mViewInfo->GetScreenWidth()); onScreen = between_incexc(GetLeftOffset(),
mLastCursorX,
GetLeftOffset() + width);
if( onScreen ) if( onScreen )
dc.Blit(mLastCursorX, 0, 1, mBacking->GetHeight(), &mBackingDC, mLastCursorX, 0); dc.Blit(mLastCursorX, 0, 1, mBacking->GetHeight(), &mBackingDC, mLastCursorX, 0);
} }
@ -1381,9 +1450,9 @@ void TrackPanel::DoDrawCursor(wxDC & dc)
return; return;
const bool const bool
onScreen = between_inclusive( mViewInfo->h, onScreen = between_incexc(mViewInfo->h,
mCursorTime, mCursorTime,
mViewInfo->h + mViewInfo->screen ); GetScreenEndTime() );
if( !onScreen ) if( !onScreen )
return; return;
@ -1396,9 +1465,10 @@ void TrackPanel::DoDrawCursor(wxDC & dc)
{ {
if( t->GetSelected() || mAx->IsFocused( t ) ) if( t->GetSelected() || mAx->IsFocused( t ) )
{ {
int y = t->GetY() - mViewInfo->vpos + 1; int y = t->GetY() - mViewInfo->vpos;
wxCoord top = y + kTopInset; wxCoord top = y + kTopMargin;
wxCoord bottom = y + t->GetHeight() - kTopInset; // Minus one more because AColor::Line includes both endpoints
wxCoord bottom = y + t->GetHeight() - kBottomMargin - 1;
// MB: warp() is not needed here as far as I know, in fact it creates a bug. Removing it fixes that. // MB: warp() is not needed here as far as I know, in fact it creates a bug. Removing it fixes that.
AColor::Line(dc, mLastCursorX, top, mLastCursorX, bottom); // <-- The whole point of this routine. AColor::Line(dc, mLastCursorX, top, mLastCursorX, bottom); // <-- The whole point of this routine.
@ -1608,12 +1678,12 @@ void TrackPanel::HandleControlKey(bool down)
void TrackPanel::HandlePageUpKey() void TrackPanel::HandlePageUpKey()
{ {
mListener->TP_ScrollWindow(mViewInfo->h + mViewInfo->screen); mListener->TP_ScrollWindow(GetScreenEndTime());
} }
void TrackPanel::HandlePageDownKey() void TrackPanel::HandlePageDownKey()
{ {
mListener->TP_ScrollWindow(mViewInfo->h - mViewInfo->screen); mListener->TP_ScrollWindow(2 * mViewInfo->h - GetScreenEndTime());
} }
void TrackPanel::HandleCursorForLastMouseEvent() void TrackPanel::HandleCursorForLastMouseEvent()
@ -2252,7 +2322,7 @@ double TrackPanel::FindScrubSpeed(double timeAtMouse) const
// and the extremes to the maximum scrub speed. // and the extremes to the maximum scrub speed.
// Width of visible track area, in time terms: // Width of visible track area, in time terms:
const double screen = mViewInfo->screen; const double screen = GetScreenEndTime() - mViewInfo->h;
const double origin = mViewInfo->h + screen / 2.0; const double origin = mViewInfo->h + screen / 2.0;
// There are various snapping zones that are this fraction of screen: // There are various snapping zones that are this fraction of screen:
@ -2299,7 +2369,7 @@ double TrackPanel::FindSeekSpeed(double timeAtMouse) const
const double extreme = std::max(1.0, mMaxScrubSpeed * ARBITRARY_MULTIPLIER); const double extreme = std::max(1.0, mMaxScrubSpeed * ARBITRARY_MULTIPLIER);
// Width of visible track area, in time terms: // Width of visible track area, in time terms:
const double screen = mViewInfo->screen; const double screen = GetScreenEndTime() - mViewInfo->h;
const double halfScreen = screen / 2.0; const double halfScreen = screen / 2.0;
const double origin = mViewInfo->h + halfScreen; const double origin = mViewInfo->h + halfScreen;
@ -2478,6 +2548,8 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event,
{ {
Track *rightTrack = NULL; Track *rightTrack = NULL;
mCapturedTrack = pTrack; mCapturedTrack = pTrack;
rect.y += kTopMargin;
rect.height -= kTopMargin + kBottomMargin;
mCapturedRect = rect; mCapturedRect = rect;
mMouseCapture=IsSelecting; mMouseCapture=IsSelecting;
@ -3352,9 +3424,11 @@ void TrackPanel::SelectionHandleDrag(wxMouseEvent & event, Track *clickedTrack)
wxRect rect = mCapturedRect; wxRect rect = mCapturedRect;
Track *pTrack = mCapturedTrack; Track *pTrack = mCapturedTrack;
// AS: Note that FindTrack will replace rect's value. if (!pTrack) {
if (!pTrack)
pTrack = FindTrack(event.m_x, event.m_y, false, false, &rect); pTrack = FindTrack(event.m_x, event.m_y, false, false, &rect);
rect.y += kTopMargin;
rect.height -= kTopMargin + kBottomMargin;
}
// Also fuhggeddaboudit if not in a track. // Also fuhggeddaboudit if not in a track.
if (!pTrack) if (!pTrack)
@ -3643,8 +3717,8 @@ void TrackPanel::HandleEnvelope(wxMouseEvent & event)
} }
mCapturedRect = rect; mCapturedRect = rect;
mCapturedRect.y += kTopInset; mCapturedRect.y += kTopMargin;
mCapturedRect.height -= kTopInset; mCapturedRect.height -= kTopMargin + kBottomMargin;
} }
// AS: if there's actually a selected track, then forward all of the // AS: if there's actually a selected track, then forward all of the
// mouse events to its envelope. // mouse events to its envelope.
@ -3670,8 +3744,6 @@ void TrackPanel::ForwardEventToTimeTrackEnvelope(wxMouseEvent & event)
Envelope *pspeedenvelope = ptimetrack->GetEnvelope(); Envelope *pspeedenvelope = ptimetrack->GetEnvelope();
wxRect envRect = mCapturedRect; wxRect envRect = mCapturedRect;
envRect.y++;
envRect.height -= 2;
double lower = ptimetrack->GetRangeLower(), upper = ptimetrack->GetRangeUpper(); double lower = ptimetrack->GetRangeLower(), upper = ptimetrack->GetRangeUpper();
const double dBRange = mViewInfo->dBr; const double dBRange = mViewInfo->dBr;
if (ptimetrack->GetDisplayLog()) { if (ptimetrack->GetDisplayLog()) {
@ -3713,8 +3785,6 @@ void TrackPanel::ForwardEventToWaveTrackEnvelope(wxMouseEvent & event)
// AS: Then forward our mouse event to the envelope. // AS: Then forward our mouse event to the envelope.
// It'll recalculate and then tell us whether or not to redraw. // It'll recalculate and then tell us whether or not to redraw.
wxRect envRect = mCapturedRect; wxRect envRect = mCapturedRect;
envRect.y++;
envRect.height -= 2;
float zoomMin, zoomMax; float zoomMin, zoomMax;
pwavetrack->GetDisplayBounds(&zoomMin, &zoomMax); pwavetrack->GetDisplayBounds(&zoomMin, &zoomMax);
needUpdate = penvelope->MouseEvent( needUpdate = penvelope->MouseEvent(
@ -3732,8 +3802,6 @@ void TrackPanel::ForwardEventToWaveTrackEnvelope(wxMouseEvent & event)
bool updateNeeded = false; bool updateNeeded = false;
if (e2) { if (e2) {
wxRect envRect = mCapturedRect; wxRect envRect = mCapturedRect;
envRect.y++;
envRect.height -= 2;
float zoomMin, zoomMax; float zoomMin, zoomMax;
pwavetrack->GetDisplayBounds(&zoomMin, &zoomMax); pwavetrack->GetDisplayBounds(&zoomMin, &zoomMax);
updateNeeded = e2->MouseEvent(event, envRect, updateNeeded = e2->MouseEvent(event, envRect,
@ -3746,8 +3814,6 @@ void TrackPanel::ForwardEventToWaveTrackEnvelope(wxMouseEvent & event)
if( (e2 = link->GetActiveEnvelope()) != 0 ) // search for any active DragPoint if( (e2 = link->GetActiveEnvelope()) != 0 ) // search for any active DragPoint
{ {
wxRect envRect = mCapturedRect; wxRect envRect = mCapturedRect;
envRect.y++;
envRect.height -= 2;
float zoomMin, zoomMax; float zoomMin, zoomMax;
pwavetrack->GetDisplayBounds(&zoomMin, &zoomMax); pwavetrack->GetDisplayBounds(&zoomMin, &zoomMax);
needUpdate |= e2->MouseEvent(event, envRect, needUpdate |= e2->MouseEvent(event, envRect,
@ -4092,8 +4158,8 @@ void TrackPanel::DoSlide(wxMouseEvent & event)
if (mouseTrack == NULL) { if (mouseTrack == NULL) {
// Allow sliding if the pointer is not over any track, but only if x is // Allow sliding if the pointer is not over any track, but only if x is
// within the bounds of the tracks area. // within the bounds of the tracks area.
int width, height; int width;
GetTracksUsableArea(&width, &height); GetTracksUsableArea(&width, NULL);
if (event.m_x >= GetLeftOffset() && event.m_x < GetLeftOffset() + width) if (event.m_x >= GetLeftOffset() && event.m_x < GetLeftOffset() + width)
mouseTrack = mCapturedTrack; mouseTrack = mCapturedTrack;
else else
@ -4383,18 +4449,16 @@ void TrackPanel::HandleZoomClick(wxMouseEvent & event)
/// Zoom drag /// Zoom drag
void TrackPanel::HandleZoomDrag(wxMouseEvent & event) void TrackPanel::HandleZoomDrag(wxMouseEvent & event)
{ {
int left, width, height; const int left = GetLeftOffset();
const int right = GetSize().x - kRightMargin - 1;
left = GetLeftOffset();
GetTracksUsableArea(&width, &height);
mZoomEnd = event.m_x; mZoomEnd = event.m_x;
if (event.m_x < left) { if (event.m_x < left) {
mZoomEnd = left; mZoomEnd = left;
} }
else if (event.m_x >= left + width - 1) { else if (event.m_x > right) {
mZoomEnd = left + width - 1; mZoomEnd = right;
} }
if (IsDragZooming()) { if (IsDragZooming()) {
@ -4405,11 +4469,8 @@ void TrackPanel::HandleZoomDrag(wxMouseEvent & event)
/// Zoom button up /// Zoom button up
void TrackPanel::HandleZoomButtonUp(wxMouseEvent & event) void TrackPanel::HandleZoomButtonUp(wxMouseEvent & event)
{ {
if (mZoomEnd < mZoomStart) { if (mZoomEnd < mZoomStart)
int temp = mZoomEnd; std::swap(mZoomStart, mZoomEnd);
mZoomEnd = mZoomStart;
mZoomStart = temp;
}
if (IsDragZooming()) if (IsDragZooming())
DragZoom(event, GetLeftOffset()); DragZoom(event, GetLeftOffset());
@ -4443,8 +4504,7 @@ void TrackPanel::DragZoom(wxMouseEvent & event, int trackLeftEdge)
{ {
double left = mViewInfo->PositionToTime(mZoomStart, trackLeftEdge); double left = mViewInfo->PositionToTime(mZoomStart, trackLeftEdge);
double right = mViewInfo->PositionToTime(mZoomEnd, trackLeftEdge); double right = mViewInfo->PositionToTime(mZoomEnd, trackLeftEdge);
double multiplier = (GetScreenEndTime() - mViewInfo->h) / (right - left);
double multiplier = mViewInfo->screen / (right - left);
if (event.ShiftDown()) if (event.ShiftDown())
multiplier = 1.0 / multiplier; multiplier = 1.0 / multiplier;
@ -4601,8 +4661,8 @@ void TrackPanel::HandleWaveTrackVZoom
bool fixedMousePoint) bool fixedMousePoint)
{ {
WaveTrack *const partner = static_cast<WaveTrack *>(tracks->GetLink(track)); WaveTrack *const partner = static_cast<WaveTrack *>(tracks->GetLink(track));
int height = track->GetHeight(); int height = track->GetHeight() - (kTopMargin + kBottomMargin);
int ypos = rect.y; int ypos = rect.y + kBorderThickness;
// Ensure start and end are in order (swap if not). // Ensure start and end are in order (swap if not).
if (zoomEnd < zoomStart) if (zoomEnd < zoomStart)
@ -4825,14 +4885,14 @@ void TrackPanel::HandleWaveTrackVZoom
namespace { namespace {
// Is the sample horizontally nearest to the cursor sufficiently separated from // Is the sample horizontally nearest to the cursor sufficiently separated from
// its neighbors that the pencil tool should be allowed to drag it? // its neighbors that the pencil tool should be allowed to drag it?
bool SampleResolutionTest(const ViewInfo &viewInfo, const WaveTrack *wt, double time, double rate) bool SampleResolutionTest(const ViewInfo &viewInfo, const WaveTrack *wt, double time, double rate, int width)
{ {
// Require more than 3 pixels per sample // Require more than 3 pixels per sample
// Round to an exact sample time // Round to an exact sample time
const double adjustedTime = wt->LongSamplesToTime(wt->TimeToLongSamples(time)); const double adjustedTime = wt->LongSamplesToTime(wt->TimeToLongSamples(time));
const wxInt64 xx = std::max(wxInt64(0), viewInfo.TimeToPosition(adjustedTime)); const wxInt64 xx = std::max(wxInt64(0), viewInfo.TimeToPosition(adjustedTime));
ZoomInfo::Intervals intervals; ZoomInfo::Intervals intervals;
viewInfo.FindIntervals(rate, intervals); viewInfo.FindIntervals(rate, intervals, width);
ZoomInfo::Intervals::const_iterator it = intervals.begin(), end = intervals.end(), prev; ZoomInfo::Intervals::const_iterator it = intervals.begin(), end = intervals.end(), prev;
wxASSERT(it != end && it->position == 0); wxASSERT(it != end && it->position == 0);
do do
@ -4874,7 +4934,9 @@ bool TrackPanel::IsSampleEditingPossible( wxMouseEvent &event, Track * t )
WaveTrack *const wt = static_cast<WaveTrack*>(t); WaveTrack *const wt = static_cast<WaveTrack*>(t);
const double rate = wt->GetRate(); const double rate = wt->GetRate();
const double time = mViewInfo->PositionToTime(event.m_x, rect.x); const double time = mViewInfo->PositionToTime(event.m_x, rect.x);
showPoints = SampleResolutionTest(*mViewInfo, wt, time, rate); int width;
GetTracksUsableArea(&width, NULL);
showPoints = SampleResolutionTest(*mViewInfo, wt, time, rate, width);
} }
//If we aren't zoomed in far enough, show a message dialog. //If we aren't zoomed in far enough, show a message dialog.
@ -4893,7 +4955,7 @@ float TrackPanel::FindSampleEditingLevel(wxMouseEvent &event, double dBRange, do
mDrawingTrack->GetDisplayBounds(&zoomMin, &zoomMax); mDrawingTrack->GetDisplayBounds(&zoomMin, &zoomMax);
const int y = event.m_y - mDrawingTrackTop; const int y = event.m_y - mDrawingTrackTop;
const int height = mDrawingTrack->GetHeight(); const int height = mDrawingTrack->GetHeight() - (kTopMargin + kBottomMargin);
const bool dB = !mDrawingTrack->GetWaveformSettings().isLinear(); const bool dB = !mDrawingTrack->GetWaveformSettings().isLinear();
float newLevel = float newLevel =
::ValueOfPixel(y, height, false, dB, dBRange, zoomMin, zoomMax); ::ValueOfPixel(y, height, false, dB, dBRange, zoomMin, zoomMax);
@ -4934,7 +4996,7 @@ void TrackPanel::HandleSampleEditingClick( wxMouseEvent & event )
/// \todo Should mCapturedTrack take the place of mDrawingTrack?? /// \todo Should mCapturedTrack take the place of mDrawingTrack??
mDrawingTrack = static_cast<WaveTrack*>(t); mDrawingTrack = static_cast<WaveTrack*>(t);
mDrawingTrackTop=rect.y; mDrawingTrackTop=rect.y + kTopMargin;
//If we are still around, we are drawing in earnest. Set some member data structures up: //If we are still around, we are drawing in earnest. Set some member data structures up:
//First, calculate the starting sample. To get this, we need the time //First, calculate the starting sample. To get this, we need the time
@ -6232,7 +6294,7 @@ void TrackPanel::HandleWheelRotation(wxMouseEvent & event)
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL #ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
if (mSmoothScrollingScrub) { if (mSmoothScrollingScrub) {
// Expand or contract about the center, ignoring mouse position // Expand or contract about the center, ignoring mouse position
center_h = mViewInfo->h + mViewInfo->screen / 2.0; center_h = mViewInfo->h + (GetScreenEndTime() - mViewInfo->h) / 2.0;
xx = mViewInfo->TimeToPosition(center_h, trackLeftEdge); xx = mViewInfo->TimeToPosition(center_h, trackLeftEdge);
} }
else else
@ -6697,8 +6759,7 @@ bool TrackPanel::HandleLabelTrackClick(LabelTrack * lTrack, wxRect &rect, wxMous
} }
mCapturedRect = rect; mCapturedRect = rect;
mCapturedRect.x += kLeftInset; mCapturedRect.width -= kRightMargin;
mCapturedRect.width -= kLeftInset;
lTrack->HandleClick(event, mCapturedRect, *mViewInfo, &mViewInfo->selectedRegion); lTrack->HandleClick(event, mCapturedRect, *mViewInfo, &mViewInfo->selectedRegion);
@ -7112,7 +7173,9 @@ bool TrackPanel::HitTestSamples(Track *track, wxRect &rect, wxMouseEvent & event
const bool dB = !wavetrack->GetWaveformSettings().isLinear(); const bool dB = !wavetrack->GetWaveformSettings().isLinear();
const double tt = mViewInfo->PositionToTime(event.m_x, rect.x); const double tt = mViewInfo->PositionToTime(event.m_x, rect.x);
if (!SampleResolutionTest(*mViewInfo, wavetrack, tt, rate)) int width;
GetTracksUsableArea(&width, NULL);
if (!SampleResolutionTest(*mViewInfo, wavetrack, tt, rate, width))
return false; return false;
// Just get one sample. // Just get one sample.
@ -7178,10 +7241,12 @@ void TrackPanel::RefreshTrack(Track *trk, bool refreshbacking)
link = trk->GetLink(); link = trk->GetLink();
} }
// subtract insets and shadows from the rectangle, but not border
// This matters because some separators do paint over the border
wxRect rect(kLeftInset, wxRect rect(kLeftInset,
-mViewInfo->vpos + trk->GetY() + kTopInset, -mViewInfo->vpos + trk->GetY() + kTopInset,
GetRect().GetWidth() - kLeftInset * 2 - 1, GetRect().GetWidth() - kLeftInset - kRightInset - kShadowThickness,
trk->GetHeight() - kTopInset - 1); trk->GetHeight() - kTopInset - kShadowThickness);
if (link) { if (link) {
rect.height += link->GetHeight(); rect.height += link->GetHeight();
@ -7332,9 +7397,9 @@ void TrackPanel::DrawEverythingElse(wxDC * dc,
if (region.Contains(0, trackRect.y, GetLeftOffset(), trackRect.height)) { if (region.Contains(0, trackRect.y, GetLeftOffset(), trackRect.height)) {
wxRect rect = trackRect; wxRect rect = trackRect;
rect.x += GetVRulerOffset(); rect.x += GetVRulerOffset();
rect.y += kTopInset; rect.y += kTopMargin;
rect.width = GetVRulerWidth(); rect.width = GetVRulerWidth();
rect.height -= (kTopInset + 2); rect.height -= (kTopMargin + kBottomMargin);
mTrackArtist->DrawVRuler(t, dc, rect); mTrackArtist->DrawVRuler(t, dc, rect);
} }
@ -7345,9 +7410,9 @@ void TrackPanel::DrawEverythingElse(wxDC * dc,
if (region.Contains(0, trackRect.y, GetLeftOffset(), trackRect.height)) { if (region.Contains(0, trackRect.y, GetLeftOffset(), trackRect.height)) {
wxRect rect = trackRect; wxRect rect = trackRect;
rect.x += GetVRulerOffset(); rect.x += GetVRulerOffset();
rect.y += kTopInset; rect.y += kTopMargin;
rect.width = GetVRulerWidth(); rect.width = GetVRulerWidth();
rect.height -= (kTopInset + 2); rect.height -= (kTopMargin + kBottomMargin);
mTrackArtist->DrawVRuler(t, dc, rect); mTrackArtist->DrawVRuler(t, dc, rect);
} }
} }
@ -7573,18 +7638,17 @@ void TrackPanel::DrawZooming(wxDC * dc, const wxRect & clip)
dc->SetPen(*wxBLACK_DASHED_PEN); dc->SetPen(*wxBLACK_DASHED_PEN);
if (mMouseCapture==IsVZooming) { if (mMouseCapture==IsVZooming) {
int width, height; rect.y = std::min(mZoomStart, mZoomEnd);
GetTracksUsableArea(&width, &height); rect.height = 1 + abs(mZoomEnd - mZoomStart);
rect.y = mZoomStart;
rect.x = GetVRulerOffset(); rect.x = GetVRulerOffset();
rect.width = width + GetVRulerWidth() + 1; //+1 extends into border rect rect.SetRight(GetSize().x - kRightMargin); // extends into border rect
rect.height = mZoomEnd - mZoomStart;
} }
else { else {
rect.x = mZoomStart; rect.x = std::min(mZoomStart, mZoomEnd);
rect.width = 1 + abs(mZoomEnd - mZoomStart);
rect.y = -1; rect.y = -1;
rect.width = mZoomEnd - mZoomStart;
rect.height = clip.height + 2; rect.height = clip.height + 2;
} }
@ -7752,9 +7816,11 @@ void TrackPanel::DrawOutsideOfTrack(Track * t, wxDC * dc, const wxRect & rect)
} }
#else #else
if (t->GetLinked()) { if (t->GetLinked()) {
// Paint the channel separator over (what would be) the shadow of the top
// channel, and the top inset of the bottom channel
side = rect; side = rect;
side.y += t->GetHeight() - 1; side.y += t->GetHeight() - kShadowThickness;
side.height = kTopInset + 1; side.height = kTopInset + kShadowThickness;
dc->DrawRectangle(side); dc->DrawRectangle(side);
} }
#endif #endif
@ -7888,20 +7954,20 @@ void TrackPanel::UpdateTrackVRuler(Track *t)
return; return;
wxRect rect(GetVRulerOffset(), wxRect rect(GetVRulerOffset(),
kTopInset, kTopMargin,
GetVRulerWidth(), GetVRulerWidth(),
t->GetHeight() - (kTopInset + 2)); t->GetHeight() - (kTopMargin + kBottomMargin));
mTrackArtist->UpdateVRuler(t, rect); mTrackArtist->UpdateVRuler(t, rect);
Track *l = t->GetLink(); Track *l = t->GetLink();
if (l) if (l)
{ {
rect.height = l->GetHeight() - (kTopInset + 2); rect.height = l->GetHeight() - (kTopMargin + kBottomMargin);
mTrackArtist->UpdateVRuler(l, rect); mTrackArtist->UpdateVRuler(l, rect);
} }
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY #ifdef EXPERIMENTAL_OUTPUT_DISPLAY
else if(MONO_WAVE_PAN(t)){ else if(MONO_WAVE_PAN(t)){
rect.height = t->GetHeight(true) - (kTopInset + 2); rect.height = t->GetHeight(true) - (kTopMargin + kBottomMargin);
mTrackArtist->UpdateVRuler(t, rect); mTrackArtist->UpdateVRuler(t, rect);
} }
#endif #endif
@ -8192,14 +8258,11 @@ void TrackPanel::OnToggle()
// Make sure selection edge is in view // Make sure selection edge is in view
void TrackPanel::ScrollIntoView(double pos) void TrackPanel::ScrollIntoView(double pos)
{ {
const int screenWidth = rint(mViewInfo->GetScreenWidth()); int w;
GetTracksUsableArea( &w, NULL );
int w, h;
GetTracksUsableArea( &w, &h );
// Or should we just set w = screenWidth ?
int pixel = mViewInfo->TimeToPosition(pos); int pixel = mViewInfo->TimeToPosition(pos);
if (pixel < 0 || pixel >= screenWidth) if (pixel < 0 || pixel >= w)
{ {
mListener->TP_ScrollWindow mListener->TP_ScrollWindow
(mViewInfo->OffsetTimeByPixels(pos, -(w / 2))); (mViewInfo->OffsetTimeByPixels(pos, -(w / 2)));
@ -9041,8 +9104,11 @@ void TrackPanel::DrawBordersAroundTrack(Track * t, wxDC * dc,
} }
#else #else
if (t->GetLinked()) { if (t->GetLinked()) {
// The given rect has had the top inset subtracted
int h1 = rect.y + t->GetHeight() - kTopInset; int h1 = rect.y + t->GetHeight() - kTopInset;
AColor::Line(*dc, vrul, h1 - 2, rect.x + rect.width - 1, h1 - 2); // h1 is the top coordinate of the second tracks' rectangle
// Draw (part of) the bottom border of the top channel and top border of the bottom
AColor::Line(*dc, vrul, h1 - kBottomMargin, rect.x + rect.width - 1, h1 - kBottomMargin);
AColor::Line(*dc, vrul, h1 + kTopInset, rect.x + rect.width - 1, h1 + kTopInset); AColor::Line(*dc, vrul, h1 + kTopInset, rect.x + rect.width - 1, h1 + kTopInset);
} }
#endif #endif
@ -9913,6 +9979,9 @@ void TrackPanel::OnSetFont(wxCommandEvent & WXUNUSED(event))
Track *TrackPanel::FindTrack(int mouseX, int mouseY, bool label, bool link, Track *TrackPanel::FindTrack(int mouseX, int mouseY, bool label, bool link,
wxRect * trackRect) wxRect * trackRect)
{ {
// If label is true, resulting rectangle OMITS left and top insets.
// If label is false, resulting rectangle INCLUDES right and top insets.
wxRect rect; wxRect rect;
rect.x = 0; rect.x = 0;
rect.y = -mViewInfo->vpos; rect.y = -mViewInfo->vpos;

View File

@ -165,6 +165,8 @@ class AUDACITY_DLL_API TrackPanel:public wxPanel {
virtual int GetLeftOffset() const { return GetLabelWidth() + 1;} virtual int GetLeftOffset() const { return GetLabelWidth() + 1;}
// Width and height, relative to upper left corner at (GetLeftOffset(), 0)
// Either argument may be NULL
virtual void GetTracksUsableArea(int *width, int *height) const; virtual void GetTracksUsableArea(int *width, int *height) const;
virtual void SelectNone(); virtual void SelectNone();
@ -232,6 +234,10 @@ class AUDACITY_DLL_API TrackPanel:public wxPanel {
virtual void DrawQuickPlayIndicator(wxDC & dc, double pos); virtual void DrawQuickPlayIndicator(wxDC & dc, double pos);
// Returns the time corresponding to the pixel column one past the track area
// (ignoring any fisheye)
virtual double GetScreenEndTime() const;
protected: protected:
virtual MixerBoard* GetMixerBoard(); virtual MixerBoard* GetMixerBoard();
/** @brief Populates the track pop-down menu with the common set of /** @brief Populates the track pop-down menu with the common set of

View File

@ -22,10 +22,9 @@ static const double gMaxZoom = 6000000;
static const double gMinZoom = 0.001; static const double gMinZoom = 0.001;
} }
ZoomInfo::ZoomInfo(double start, double screenDuration, double pixelsPerSecond) ZoomInfo::ZoomInfo(double start, double pixelsPerSecond)
: vpos(0) : vpos(0)
, h(start) , h(start)
, screen(screenDuration)
, zoom(pixelsPerSecond) , zoom(pixelsPerSecond)
{ {
UpdatePrefs(); UpdatePrefs();
@ -84,12 +83,12 @@ void ZoomInfo::ZoomBy(double multiplier)
} }
void ZoomInfo::FindIntervals void ZoomInfo::FindIntervals
(double /*rate*/, Intervals &results, wxInt64 origin) const (double /*rate*/, Intervals &results, wxInt64 width, wxInt64 origin) const
{ {
results.clear(); results.clear();
results.reserve(2); results.reserve(2);
const wxInt64 rightmost(origin + (0.5 + screen * zoom)); const wxInt64 rightmost(origin + (0.5 + width));
wxASSERT(origin <= rightmost); wxASSERT(origin <= rightmost);
{ {
results.push_back(Interval(origin, zoom, false)); results.push_back(Interval(origin, zoom, false));
@ -101,10 +100,10 @@ void ZoomInfo::FindIntervals
} }
ViewInfo::ViewInfo(double start, double screenDuration, double pixelsPerSecond) ViewInfo::ViewInfo(double start, double screenDuration, double pixelsPerSecond)
: ZoomInfo(start, screenDuration, pixelsPerSecond) : ZoomInfo(start, pixelsPerSecond)
, selectedRegion() , selectedRegion()
, track(NULL) , track(NULL)
, total(screen) , total(screenDuration)
, sbarH(0) , sbarH(0)
, sbarScreen(1) , sbarScreen(1)
, sbarTotal(1) , sbarTotal(1)
@ -114,12 +113,12 @@ ViewInfo::ViewInfo(double start, double screenDuration, double pixelsPerSecond)
{ {
} }
void ViewInfo::SetBeforeScreenWidth(wxInt64 width, double lowerBoundTime) void ViewInfo::SetBeforeScreenWidth(wxInt64 beforeWidth, wxInt64 screenWidth, double lowerBoundTime)
{ {
h = h =
std::max(lowerBoundTime, std::max(lowerBoundTime,
std::min(total - screen, std::min(total - screenWidth / zoom,
width / zoom)); beforeWidth / zoom));
} }
void ViewInfo::WriteXMLAttributes(XMLWriter &xmlFile) void ViewInfo::WriteXMLAttributes(XMLWriter &xmlFile)

View File

@ -29,7 +29,7 @@ class Track;
class AUDACITY_DLL_API ZoomInfo class AUDACITY_DLL_API ZoomInfo
{ {
public: public:
ZoomInfo(double start, double duration, double pixelsPerSecond); ZoomInfo(double start, double pixelsPerSecond);
~ZoomInfo(); ~ZoomInfo();
void UpdatePrefs(); void UpdatePrefs();
@ -38,8 +38,6 @@ public:
double h; // h pos in secs double h; // h pos in secs
double screen; // screen width in secs
protected: protected:
double zoom; // pixels per second double zoom; // pixels per second
@ -53,7 +51,7 @@ public:
double PositionToTime(wxInt64 position, double PositionToTime(wxInt64 position,
wxInt64 origin = 0 wxInt64 origin = 0
, bool ignoreFisheye = false , bool ignoreFisheye = false
) const; ) const;
// do NOT use this once to convert a duration to a pixel width! // do NOT use this once to convert a duration to a pixel width!
// Instead, call twice to convert start and end positions, // Instead, call twice to convert start and end positions,
@ -62,7 +60,7 @@ public:
wxInt64 TimeToPosition(double time, wxInt64 TimeToPosition(double time,
wxInt64 origin = 0 wxInt64 origin = 0
, bool ignoreFisheye = false , bool ignoreFisheye = false
) const; ) const;
double OffsetTimeByPixels(double time, wxInt64 offset) const double OffsetTimeByPixels(double time, wxInt64 offset) const
{ {
@ -72,13 +70,6 @@ public:
bool ZoomInAvailable() const; bool ZoomInAvailable() const;
bool ZoomOutAvailable() 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() static double GetDefaultZoom()
{ return 44100.0 / 512.0; } { return 44100.0 / 512.0; }
@ -107,7 +98,7 @@ public:
// first entry equals origin. // first entry equals origin.
// @param origin specifies the pixel position corresponding to time ViewInfo::h. // @param origin specifies the pixel position corresponding to time ViewInfo::h.
void FindIntervals void FindIntervals
(double rate, Intervals &results, wxInt64 origin = 0) const; (double rate, Intervals &results, wxInt64 width, wxInt64 origin = 0) const;
enum FisheyeState { enum FisheyeState {
HIDDEN, HIDDEN,
@ -120,7 +111,7 @@ public:
// Return true if the mouse position is anywhere in the fisheye // Return true if the mouse position is anywhere in the fisheye
// origin specifies the pixel corresponding to time h // origin specifies the pixel corresponding to time h
bool InFisheye(wxInt64 position, wxInt64 WXUNUSED(origin = 0)) const bool InFisheye(wxInt64 /*position*/, wxInt64 WXUNUSED(origin = 0)) const
{return false;} // stub {return false;} // stub
// These accessors ignore the fisheye hiding state. // These accessors ignore the fisheye hiding state.
@ -143,14 +134,11 @@ public:
{ {
return h * zoom; return h * zoom;
} }
void SetBeforeScreenWidth(wxInt64 width, double lowerBoundTime = 0.0); void SetBeforeScreenWidth(wxInt64 beforeWidth, wxInt64 screenWidth, double lowerBoundTime = 0.0);
double GetTotalWidth() const double GetTotalWidth() const
{ return total * zoom; } { return total * zoom; }
bool ZoomedAll() const
{ return screen >= total; }
// Current selection // Current selection
SelectedRegion selectedRegion; SelectedRegion selectedRegion;

View File

@ -2908,7 +2908,7 @@ void EqualizationPanel::OnPaint(wxPaintEvent & WXUNUSED(event))
memDC.SetPen(*wxBLACK_PEN); memDC.SetPen(*wxBLACK_PEN);
if( mEffect->mDraw->GetValue() ) if( mEffect->mDraw->GetValue() )
{ {
mEffect->mEnvelope->DrawPoints(memDC, mEnvRect, ZoomInfo(0.0, 1.0, mEnvRect.width-1), false, 0.0, mEffect->mEnvelope->DrawPoints(memDC, mEnvRect, ZoomInfo(0.0, mEnvRect.width-1), false, 0.0,
mEffect->mdBMin, mEffect->mdBMax); mEffect->mdBMin, mEffect->mdBMax);
} }
@ -2927,7 +2927,7 @@ void EqualizationPanel::OnMouseEvent(wxMouseEvent & event)
CaptureMouse(); CaptureMouse();
} }
if (mEffect->mEnvelope->MouseEvent(event, mEnvRect, ZoomInfo(0.0, 1.0, mEnvRect.width), if (mEffect->mEnvelope->MouseEvent(event, mEnvRect, ZoomInfo(0.0, mEnvRect.width),
false, 0.0, false, 0.0,
mEffect->mdBMin, mEffect->mdBMax)) mEffect->mdBMin, mEffect->mdBMax))
{ {

View File

@ -267,6 +267,7 @@ PrefsDialog::PrefsDialog
S.EndVerticalLay(); S.EndVerticalLay();
S.AddStandardButtons(eOkButton | eCancelButton | eApplyButton); S.AddStandardButtons(eOkButton | eCancelButton | eApplyButton);
static_cast<wxButton*>(wxWindow::FindWindowById(wxID_OK, this))->SetDefault();
if (mUniquePage && !mUniquePage->ShowsApplyButton()) { if (mUniquePage && !mUniquePage->ShowsApplyButton()) {
wxWindow *const applyButton = wxWindow *const applyButton =

View File

@ -56,6 +56,8 @@ AttachableScrollBar::~AttachableScrollBar(void)
// Essentially a float to int conversion. // Essentially a float to int conversion.
void AttachableScrollBar::SetScrollBarFromViewInfo() void AttachableScrollBar::SetScrollBarFromViewInfo()
{ {
// fix me
#if 0
ViewInfo & mViewInfo = *mpViewInfo; ViewInfo & mViewInfo = *mpViewInfo;
mViewInfo.sbarTotal = (int) (mViewInfo.GetTotalWidth()); mViewInfo.sbarTotal = (int) (mViewInfo.GetTotalWidth());
@ -64,11 +66,14 @@ void AttachableScrollBar::SetScrollBarFromViewInfo()
SetScrollbar(mViewInfo.sbarH, mViewInfo.sbarScreen, SetScrollbar(mViewInfo.sbarH, mViewInfo.sbarScreen,
mViewInfo.sbarTotal, mViewInfo.sbarScreen, TRUE); mViewInfo.sbarTotal, mViewInfo.sbarScreen, TRUE);
#endif
} }
// Essentially an int to float conversion. // Essentially an int to float conversion.
void AttachableScrollBar::SetViewInfoFromScrollBar() void AttachableScrollBar::SetViewInfoFromScrollBar()
{ {
// fixme
#if 0
ViewInfo & mViewInfo = *mpViewInfo; ViewInfo & mViewInfo = *mpViewInfo;
int hlast = mViewInfo.sbarH; int hlast = mViewInfo.sbarH;
@ -76,7 +81,8 @@ void AttachableScrollBar::SetViewInfoFromScrollBar()
mViewInfo.sbarH = GetThumbPosition(); mViewInfo.sbarH = GetThumbPosition();
if (mViewInfo.sbarH != hlast) if (mViewInfo.sbarH != hlast)
mViewInfo.SetBeforeScreenWidth(mViewInfo.sbarH); mViewInfo.SetBeforeScreenWidth(mViewInfo.sbarH);
#endif
} }
// Used to associated a ViewInfo structure with a scrollbar. // Used to associated a ViewInfo structure with a scrollbar.

View File

@ -1936,8 +1936,8 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
// Keep Quick-Play within usable track area. // Keep Quick-Play within usable track area.
TrackPanel *tp = mProject->GetTrackPanel(); TrackPanel *tp = mProject->GetTrackPanel();
int mousePosX, width, height; int mousePosX, width;
tp->GetTracksUsableArea(&width, &height); tp->GetTracksUsableArea(&width, NULL);
mousePosX = std::max(evt.GetX(), tp->GetLeftOffset()); mousePosX = std::max(evt.GetX(), tp->GetLeftOffset());
mousePosX = std::min(mousePosX, tp->GetLeftOffset() + width - 1); mousePosX = std::min(mousePosX, tp->GetLeftOffset() + width - 1);