Move drawing (and updating) code for vertical rulers

This commit is contained in:
Paul Licameli 2018-11-03 01:15:22 -04:00
parent fccb832e2d
commit b45d1387dc
14 changed files with 633 additions and 532 deletions

View File

@ -43,67 +43,19 @@ audio tracks.
#include "Experimental.h"
#include "float_cast.h"
#include <math.h>
#include <float.h>
#include <limits>
#include <wx/utils.h>
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#include <wx/brush.h>
#include <wx/colour.h>
#include <wx/dc.h>
#include <wx/dcmemory.h>
#include <wx/gdicmn.h>
#include <wx/graphics.h>
#include <wx/image.h>
#include <wx/pen.h>
#include <wx/log.h>
#include <wx/datetime.h>
#ifdef USE_MIDI
#include "NoteTrack.h"
#endif // USE_MIDI
#include "AColor.h"
#include "BlockFile.h"
#include "Envelope.h"
#include "EnvelopeEditor.h"
#include "NumberScale.h"
#include "WaveClip.h"
#include "LabelTrack.h"
#include "TimeTrack.h"
#include "AllThemeResources.h"
#include "Prefs.h"
#include "prefs/GUIPrefs.h"
#include "TrackPanelDrawingContext.h"
#include "ViewInfo.h"
#include "WaveTrack.h"
#include "prefs/GUISettings.h"
#include "prefs/SpectrogramSettings.h"
#include "prefs/TracksPrefs.h"
#include "prefs/WaveformSettings.h"
#include "Spectrum.h"
#include "ViewInfo.h"
#include "widgets/Ruler.h"
#include "AllThemeResources.h"
#include "TrackPanelDrawingContext.h"
#include "tracks/labeltrack/ui/LabelTrackView.h"
#include "tracks/ui/TrackView.h"
#undef PROFILE_WAVEFORM
#ifdef PROFILE_WAVEFORM
#ifdef __WXMSW__
#include <time.h>
#else
#include <sys/time.h>
#endif
double gWaveformTimeTotal = 0;
int gWaveformTimeCount = 0;
#endif
#include <wx/dc.h>
TrackArtist::TrackArtist( TrackPanel *parent_ )
: parent( parent_ )
@ -113,7 +65,6 @@ TrackArtist::TrackArtist( TrackPanel *parent_ )
mSampleDisplay = 1;// Stem plots by default.
SetColours(0);
vruler = std::make_unique<Ruler>();
UpdatePrefs();
}
@ -279,448 +230,6 @@ void TrackArt::DrawTrackName( TrackPanelDrawingContext &context, const Track * t
dc.DrawText (t->GetName(), rect.x+15, rect.y+3); // move right 15 pixels to avoid overwriting <- symbol
}
void TrackArt::DrawVRuler
( TrackPanelDrawingContext &context, const Track *t, const wxRect & rect_,
bool bSelected )
{
auto rect = rect_;
--rect.width;
auto dc = &context.dc;
bool highlight = false;
#ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
highlight = rect.Contains(context.lastState.GetPosition());
#endif
// Label and Time tracks do not have a vruler
// But give it a beveled area
t->TypeSwitch(
[&](const LabelTrack *) {
wxRect bev = rect;
bev.Inflate(-1, 0);
bev.width += 1;
AColor::BevelTrackInfo(*dc, true, bev);
},
[&](const TimeTrack *) {
wxRect bev = rect;
bev.Inflate(-1, 0);
bev.width += 1;
AColor::BevelTrackInfo(*dc, true, bev);
// Right align the ruler
wxRect rr = rect;
rr.width--;
if (t->vrulerSize.GetWidth() < rect.GetWidth()) {
int adj = rr.GetWidth() - t->vrulerSize.GetWidth();
rr.x += adj;
rr.width -= adj;
}
const auto artist = TrackArtist::Get( context );
artist->UpdateVRuler(t, rr);
const auto &vruler = artist->vruler;
vruler->SetTickColour( theTheme.Colour( clrTrackPanelText ));
vruler->Draw(*dc);
},
[&](const WaveTrack *) {
// All waves have a ruler in the info panel
// The ruler needs a bevelled surround.
wxRect bev = rect;
bev.Inflate(-1, 0);
bev.width += 1;
AColor::BevelTrackInfo(*dc, true, bev, highlight);
// Right align the ruler
wxRect rr = rect;
rr.width--;
if (t->vrulerSize.GetWidth() < rect.GetWidth()) {
int adj = rr.GetWidth() - t->vrulerSize.GetWidth();
rr.x += adj;
rr.width -= adj;
}
const auto artist = TrackArtist::Get( context );
artist->UpdateVRuler(t, rr);
const auto &vruler = artist->vruler;
vruler->SetTickColour( theTheme.Colour( clrTrackPanelText ));
vruler->Draw(*dc);
}
#ifdef USE_MIDI
,
[&](const NoteTrack *track) {
// The note track draws a vertical keyboard to label pitches
const auto artist = TrackArtist::Get( context );
artist->UpdateVRuler(t, rect);
dc->SetPen(highlight ? AColor::uglyPen : *wxTRANSPARENT_PEN);
dc->SetBrush(*wxWHITE_BRUSH);
wxRect bev = rect;
bev.x++;
bev.width--;
dc->DrawRectangle(bev);
rect.y += 1;
rect.height -= 1;
NoteTrackDisplayData data = NoteTrackDisplayData(track, rect);
wxPen hilitePen;
hilitePen.SetColour(120, 120, 120);
wxBrush blackKeyBrush;
blackKeyBrush.SetColour(70, 70, 70);
dc->SetBrush(blackKeyBrush);
int fontSize = 10;
#ifdef __WXMSW__
fontSize = 8;
#endif
wxFont labelFont(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
dc->SetFont(labelFont);
int octave = 0;
int obottom = data.GetOctaveBottom(octave);
int marg = data.GetNoteMargin();
while (obottom >= rect.y) {
dc->SetPen(*wxBLACK_PEN);
for (int white = 0; white < 7; white++) {
int pos = data.GetWhitePos(white);
if (obottom - pos > rect.y + marg + 1 &&
// don't draw too close to margin line -- it's annoying
obottom - pos < rect.y + rect.height - marg - 3)
AColor::Line(*dc, rect.x, obottom - pos,
rect.x + rect.width, obottom - pos);
}
wxRect br = rect;
br.height = data.GetPitchHeight(1);
br.x++;
br.width = 17;
for (int black = 0; black < 5; black++) {
br.y = obottom - data.GetBlackPos(black);
if (br.y > rect.y + marg - 2 && br.y + br.height < rect.y + rect.height - marg) {
dc->SetPen(hilitePen);
dc->DrawRectangle(br);
dc->SetPen(*wxBLACK_PEN);
AColor::Line(*dc,
br.x + 1, br.y + br.height - 1,
br.x + br.width - 1, br.y + br.height - 1);
AColor::Line(*dc,
br.x + br.width - 1, br.y + 1,
br.x + br.width - 1, br.y + br.height - 1);
}
}
if (octave >= 1 && octave <= 10) {
wxString s;
// ISO standard: A440 is in the 4th octave, denoted
// A4 <- the "4" should be a subscript.
s.Printf(wxT("C%d"), octave - 1);
wxCoord width, height;
dc->GetTextExtent(s, &width, &height);
if (obottom - height + 4 > rect.y &&
obottom + 4 < rect.y + rect.height) {
dc->SetTextForeground(wxColour(60, 60, 255));
dc->DrawText(s, rect.x + rect.width - width,
obottom - height + 2);
}
}
obottom = data.GetOctaveBottom(++octave);
}
// draw lines delineating the out-of-bounds margins
dc->SetPen(*wxBLACK_PEN);
// you would think the -1 offset here should be -2 to match the
// adjustment to rect.y (see above), but -1 produces correct output
AColor::Line(*dc, rect.x, rect.y + marg - 1, rect.x + rect.width, rect.y + marg - 1);
// since the margin gives us the bottom of the line,
// the extra -1 gets us to the top
AColor::Line(*dc, rect.x, rect.y + rect.height - marg - 1,
rect.x + rect.width, rect.y + rect.height - marg - 1);
}
#endif // USE_MIDI
);
}
void TrackArtist::UpdateVRuler(const Track *t, const wxRect & rect)
{
auto update = t->TypeSwitch<bool>(
[] (const LabelTrack *) {
// Label tracks do not have a vruler
return false;
},
[&](const TimeTrack *tt) {
float min, max;
min = tt->GetRangeLower() * 100.0;
max = tt->GetRangeUpper() * 100.0;
vruler->SetDbMirrorValue( 0.0 );
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->SetUnits(wxT(""));
vruler->SetLabelEdges(false);
vruler->SetLog(tt->GetDisplayLog());
return true;
},
[&](const WaveTrack *wt) {
// All waves have a ruler in the info panel
// The ruler needs a bevelled surround.
const float dBRange =
wt->GetWaveformSettings().dBRange;
const int display = wt->GetDisplay();
if (display == WaveTrackViewConstants::Waveform) {
WaveformSettings::ScaleType scaleType =
wt->GetWaveformSettings().scaleType;
if (scaleType == WaveformSettings::stLinear) {
// Waveform
float min, max;
wt->GetDisplayBounds(&min, &max);
if (wt->GetLastScaleType() != scaleType &&
wt->GetLastScaleType() != -1)
{
// do a translation into the linear space
wt->SetLastScaleType();
wt->SetLastdBRange();
float sign = (min >= 0 ? 1 : -1);
if (min != 0.) {
min = DB_TO_LINEAR(fabs(min) * dBRange - dBRange);
if (min < 0.0)
min = 0.0;
min *= sign;
}
sign = (max >= 0 ? 1 : -1);
if (max != 0.) {
max = DB_TO_LINEAR(fabs(max) * dBRange - dBRange);
if (max < 0.0)
max = 0.0;
max *= sign;
}
wt->SetDisplayBounds(min, max);
}
vruler->SetDbMirrorValue( 0.0 );
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->SetUnits(wxT(""));
vruler->SetLabelEdges(false);
vruler->SetLog(false);
}
else {
wxASSERT(scaleType == WaveformSettings::stLogarithmic);
scaleType = WaveformSettings::stLogarithmic;
vruler->SetUnits(wxT(""));
float min, max;
wt->GetDisplayBounds(&min, &max);
float lastdBRange;
if (wt->GetLastScaleType() != scaleType &&
wt->GetLastScaleType() != -1)
{
// do a translation into the dB space
wt->SetLastScaleType();
wt->SetLastdBRange();
float sign = (min >= 0 ? 1 : -1);
if (min != 0.) {
min = (LINEAR_TO_DB(fabs(min)) + dBRange) / dBRange;
if (min < 0.0)
min = 0.0;
min *= sign;
}
sign = (max >= 0 ? 1 : -1);
if (max != 0.) {
max = (LINEAR_TO_DB(fabs(max)) + dBRange) / dBRange;
if (max < 0.0)
max = 0.0;
max *= sign;
}
wt->SetDisplayBounds(min, max);
}
else if (dBRange != (lastdBRange = wt->GetLastdBRange())) {
wt->SetLastdBRange();
// Remap the max of the scale
float newMax = max;
// This commented out code is problematic.
// min and max may be correct, and this code cause them to change.
#ifdef ONLY_LABEL_POSITIVE
const float sign = (max >= 0 ? 1 : -1);
if (max != 0.) {
// Ugh, duplicating from TrackPanel.cpp
#define ZOOMLIMIT 0.001f
const float extreme = LINEAR_TO_DB(2);
// recover dB value of max
const float dB = std::min(extreme, (float(fabs(max)) * lastdBRange - lastdBRange));
// find NEW scale position, but old max may get trimmed if the db limit rises
// Don't trim it to zero though, but leave max and limit distinct
newMax = sign * std::max(ZOOMLIMIT, (dBRange + dB) / dBRange);
// Adjust the min of the scale if we can,
// so the db Limit remains where it was on screen, but don't violate extremes
if (min != 0.)
min = std::max(-extreme, newMax * min / max);
}
#endif
wt->SetDisplayBounds(min, newMax);
}
// Old code was if ONLY_LABEL_POSITIVE were defined.
// it uses the +1 to 0 range only.
// the enabled code uses +1 to -1, and relies on set ticks labelling knowing about
// the dB scale.
#ifdef ONLY_LABEL_POSITIVE
if (max > 0) {
#endif
int top = 0;
float topval = 0;
int bot = rect.height;
float botval = -dBRange;
#ifdef ONLY_LABEL_POSITIVE
if (min < 0) {
bot = top + (int)((max / (max - min))*(bot - top));
min = 0;
}
if (max > 1) {
top += (int)((max - 1) / (max - min) * (bot - top));
max = 1;
}
if (max < 1 && max > 0)
topval = -((1 - max) * dBRange);
if (min > 0) {
botval = -((1 - min) * dBRange);
}
#else
topval = -((1 - max) * dBRange);
botval = -((1 - min) * dBRange);
vruler->SetDbMirrorValue( dBRange );
#endif
vruler->SetBounds(rect.x, rect.y + top, rect.x + rect.width, rect.y + bot - 1);
vruler->SetOrientation(wxVERTICAL);
vruler->SetRange(topval, botval);
#ifdef ONLY_LABEL_POSITIVE
}
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->SetLabelEdges(true);
vruler->SetLog(false);
}
}
else {
wxASSERT(display == WaveTrackViewConstants::Spectrum);
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
float minFreq, maxFreq;
wt->GetSpectrumBounds(&minFreq, &maxFreq);
vruler->SetDbMirrorValue( 0.0 );
switch (settings.scaleType) {
default:
wxASSERT(false);
case SpectrogramSettings::stLinear:
{
// Spectrum
if (rect.height < 60)
return false;
/*
draw the ruler
we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
and append to the numbers a "k"
*/
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
vruler->SetOrientation(wxVERTICAL);
vruler->SetFormat(Ruler::RealFormat);
vruler->SetLabelEdges(true);
// use kHz in scale, if appropriate
if (maxFreq >= 2000) {
vruler->SetRange((maxFreq / 1000.), (minFreq / 1000.));
vruler->SetUnits(wxT("k"));
}
else {
// use Hz
vruler->SetRange((int)(maxFreq), (int)(minFreq));
vruler->SetUnits(wxT(""));
}
vruler->SetLog(false);
}
break;
case SpectrogramSettings::stLogarithmic:
case SpectrogramSettings::stMel:
case SpectrogramSettings::stBark:
case SpectrogramSettings::stErb:
case SpectrogramSettings::stPeriod:
{
// SpectrumLog
if (rect.height < 10)
return false;
/*
draw the ruler
we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
and append to the numbers a "k"
*/
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
vruler->SetOrientation(wxVERTICAL);
vruler->SetFormat(Ruler::IntFormat);
vruler->SetLabelEdges(true);
vruler->SetRange(maxFreq, minFreq);
vruler->SetUnits(wxT(""));
vruler->SetLog(true);
NumberScale scale(
wt->GetSpectrogramSettings().GetScale( minFreq, maxFreq )
.Reversal() );
vruler->SetNumberScale(&scale);
}
break;
}
}
return true;
}
#ifdef USE_MIDI
,
[&](const NoteTrack *) {
// The note track isn't drawing a ruler at all!
// But it needs to!
vruler->SetBounds(rect.x, rect.y, rect.x + 1, rect.y + rect.height-1);
vruler->SetOrientation(wxVERTICAL);
return true;
}
#endif // USE_MIDI
);
if (update)
vruler->GetMaxSize(&t->vrulerSize.x, &t->vrulerSize.y);
}
/// Takes a value between min and max and returns a value between
/// height and 0
/// \todo Should this function move int GuiWaveTrack where it can

View File

@ -28,10 +28,8 @@
class wxRect;
class NoteTrack;
class TrackList;
class TrackPanel;
class Ruler;
class SelectedRegion;
class Track;
class TrackPanel;
@ -39,8 +37,6 @@ struct TrackPanelDrawingContext;
class ZoomInfo;
namespace TrackArt {
void DrawVRuler(TrackPanelDrawingContext &context,
const Track *t, const wxRect & rect, bool bSelected );
void DrawTrackNames(TrackPanelDrawingContext &context,
const TrackList *tracks,
@ -72,6 +68,7 @@ public:
PassTracks,
PassMargins,
PassBorders,
PassControls,
NPasses
};
@ -93,8 +90,6 @@ public:
void UpdatePrefs() override;
void UpdateSelectedPrefs( int id ) override;
void UpdateVRuler(const Track *t, const wxRect & rect);
TrackPanel *parent;
// Preference values
@ -126,8 +121,6 @@ public:
wxPen muteClippedPen;
wxPen blankSelectedPen;
std::unique_ptr<Ruler> vruler;
#ifdef EXPERIMENTAL_FFT_Y_GRID
bool fftYGridOld;
#endif //EXPERIMENTAL_FFT_Y_GRID

View File

@ -944,23 +944,22 @@ void TrackPanel::DrawEverythingElse(TrackPanelDrawingContext &context,
+ IsVisibleTrack{ GetProject() } ) {
const auto channels = TrackList::Channels(leaderTrack);
bool focused = false;
wxRect teamRect = trackRect;
teamRect.height = 0;
trackRect.height = 0;
bool first = true;
for (auto channel : channels) {
focused = focused || mAx->IsFocused(channel);
auto &view = TrackView::Get( *channel );
if (first)
first = false,
teamRect.y = view.GetY() - mViewInfo->vpos + kTopMargin;
teamRect.height += view.GetHeight();
trackRect.y = view.GetY() - mViewInfo->vpos + kTopMargin;
trackRect.height += view.GetHeight();
}
if (focused) {
focusRect = teamRect;
focusRect = trackRect;
focusRect.height -= kSeparatorThickness;
}
DrawOutside(context, leaderTrack, teamRect);
DrawOutside(context, leaderTrack, trackRect);
// Believe it or not, we can speed up redrawing if we don't
// redraw the vertical ruler when only the waveform data has
@ -971,24 +970,6 @@ void TrackPanel::DrawEverythingElse(TrackPanelDrawingContext &context,
// wxPrintf(wxT("Update Region: %d %d %d %d\n"),
// rbox.x, rbox.y, rbox.width, rbox.height);
#endif
for (auto channel : channels) {
auto &view = TrackView::Get( *channel );
bool bSelected = channel->GetSelected();
channel = channel->SubstitutePendingChangedTrack().get();
trackRect.y = view.GetY() - mViewInfo->vpos + kTopMargin;
trackRect.height = view.GetHeight();
if (region.Contains(
0, trackRect.y, mViewInfo->GetLeftOffset(), trackRect.height)) {
wxRect rect{
mViewInfo->GetVRulerOffset(),
trackRect.y,
mViewInfo->GetVRulerWidth() + 1,
trackRect.height - kSeparatorThickness
};
TrackArt::DrawVRuler(context, channel, rect, bSelected);
}
}
}
auto target = Target();
@ -1086,7 +1067,7 @@ void TrackPanel::HighlightFocusedTrack(wxDC * dc, const wxRect & rect)
void TrackPanel::UpdateVRulers()
{
for (auto t : GetTracks()->Any< const WaveTrack >())
for (auto t : GetTracks()->Any< WaveTrack >())
UpdateTrackVRuler(t);
UpdateVRulerSize();
@ -1100,7 +1081,7 @@ void TrackPanel::UpdateVRuler(Track *t)
UpdateVRulerSize();
}
void TrackPanel::UpdateTrackVRuler(const Track *t)
void TrackPanel::UpdateTrackVRuler(Track *t)
{
wxASSERT(t);
if (!t)
@ -1115,7 +1096,7 @@ void TrackPanel::UpdateTrackVRuler(const Track *t)
for (auto channel : TrackList::Channels(t)) {
auto &view = TrackView::Get( *channel );
rect.height = view.GetHeight() - (kTopMargin + kBottomMargin);
mTrackArtist->UpdateVRuler(channel, rect);
TrackVRulerControls::Get( *channel ).UpdateRuler( rect );
}
}

View File

@ -131,7 +131,7 @@ class AUDACITY_DLL_API TrackPanel final
void UpdateVRulers();
void UpdateVRuler(Track *t);
void UpdateTrackVRuler(const Track *t);
void UpdateTrackVRuler(Track *t);
void UpdateVRulerSize();
protected:

View File

@ -13,6 +13,38 @@ Paul Licameli split from TrackPanel.cpp
#include "../../../HitTestResult.h"
#include "../../../AColor.h"
#include "../../../TrackArtist.h"
#include "../../../TrackPanelDrawingContext.h"
LabelTrackVRulerControls::~LabelTrackVRulerControls()
{
}
void LabelTrackVRulerControls::Draw(
TrackPanelDrawingContext &context,
const wxRect &rect_, unsigned iPass )
{
TrackVRulerControls::Draw( context, rect_, iPass );
// Draw on a later pass because the bevel overpaints one pixel
// out of bounds on the bottom
if ( iPass == TrackArtist::PassControls ) {
auto rect = rect_;
--rect.width;
--rect.height;
auto dc = &context.dc;
wxRect bev = rect;
bev.Inflate(-1, 0);
bev.width += 1;
AColor::BevelTrackInfo(*dc, true, bev);
}
}
void LabelTrackVRulerControls::UpdateRuler( const wxRect &rect )
{
// Label tracks do not have a vruler
// do nothing
}

View File

@ -26,6 +26,17 @@ public:
LabelTrackVRulerControls( const std::shared_ptr<TrackView> &pTrackView )
: TrackVRulerControls( pTrackView ) {}
~LabelTrackVRulerControls();
private:
// TrackPanelDrawable implementation
void Draw(
TrackPanelDrawingContext &context,
const wxRect &rect, unsigned iPass ) override;
// TrackVRulerControls implementation
void UpdateRuler( const wxRect &rect ) override;
};
#endif

View File

@ -19,8 +19,15 @@ Paul Licameli split from TrackPanel.cpp
#include "../../../../NoteTrack.h"
#include "../../../../ProjectHistory.h"
#include "../../../../RefreshCode.h"
#include "../../../../TrackArtist.h"
#include "../../../../TrackPanelMouseEvent.h"
#include "../../../../AColor.h"
#include "../../../../Experimental.h"
#include "../../../../TrackPanelDrawingContext.h"
#include "../../../../widgets/Ruler.h"
#include <wx/dc.h>
///////////////////////////////////////////////////////////////////////////////
NoteTrackVRulerControls::~NoteTrackVRulerControls()
@ -86,4 +93,140 @@ unsigned NoteTrackVRulerControls::HandleWheelRotation
return RefreshCell | UpdateVRuler;
}
void NoteTrackVRulerControls::Draw(
TrackPanelDrawingContext &context,
const wxRect &rect_, unsigned iPass )
{
TrackVRulerControls::Draw( context, rect_, iPass );
// Draw on a later pass like other vertical rulers,
// although the bevel is done a little differently
if ( iPass == TrackArtist::PassControls ) {
// The note track draws a vertical keyboard to label pitches
auto track = std::static_pointer_cast<NoteTrack>( FindTrack() );
if ( !track )
return;
auto rect = rect_;
--rect.width;
--rect.height;
bool highlight = false;
#ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
highlight = rect.Contains(context.lastState.GetPosition());
#endif
const auto artist = TrackArtist::Get( context );
UpdateRuler(rect);
auto dc = &context.dc;
dc->SetPen(highlight ? AColor::uglyPen : *wxTRANSPARENT_PEN);
dc->SetBrush(*wxWHITE_BRUSH);
wxRect bev = rect;
bev.x++;
bev.width--;
dc->DrawRectangle(bev);
rect.y += 1;
rect.height -= 1;
NoteTrackDisplayData data{ track.get(), rect };
wxPen hilitePen;
hilitePen.SetColour(120, 120, 120);
wxBrush blackKeyBrush;
blackKeyBrush.SetColour(70, 70, 70);
dc->SetBrush(blackKeyBrush);
int fontSize = 10;
#ifdef __WXMSW__
fontSize = 8;
#endif
wxFont labelFont(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
dc->SetFont(labelFont);
int octave = 0;
int obottom = data.GetOctaveBottom(octave);
int marg = data.GetNoteMargin();
while (obottom >= rect.y) {
dc->SetPen(*wxBLACK_PEN);
for (int white = 0; white < 7; white++) {
int pos = data.GetWhitePos(white);
if (obottom - pos > rect.y + marg + 1 &&
// don't draw too close to margin line -- it's annoying
obottom - pos < rect.y + rect.height - marg - 3)
AColor::Line(*dc, rect.x, obottom - pos,
rect.x + rect.width, obottom - pos);
}
wxRect br = rect;
br.height = data.GetPitchHeight(1);
br.x++;
br.width = 17;
for (int black = 0; black < 5; black++) {
br.y = obottom - data.GetBlackPos(black);
if (br.y > rect.y + marg - 2 && br.y + br.height < rect.y + rect.height - marg) {
dc->SetPen(hilitePen);
dc->DrawRectangle(br);
dc->SetPen(*wxBLACK_PEN);
AColor::Line(*dc,
br.x + 1, br.y + br.height - 1,
br.x + br.width - 1, br.y + br.height - 1);
AColor::Line(*dc,
br.x + br.width - 1, br.y + 1,
br.x + br.width - 1, br.y + br.height - 1);
}
}
if (octave >= 1 && octave <= 10) {
wxString s;
// ISO standard: A440 is in the 4th octave, denoted
// A4 <- the "4" should be a subscript.
s.Printf(wxT("C%d"), octave - 1);
wxCoord width, height;
dc->GetTextExtent(s, &width, &height);
if (obottom - height + 4 > rect.y &&
obottom + 4 < rect.y + rect.height) {
dc->SetTextForeground(wxColour(60, 60, 255));
dc->DrawText(s, rect.x + rect.width - width,
obottom - height + 2);
}
}
obottom = data.GetOctaveBottom(++octave);
}
// draw lines delineating the out-of-bounds margins
dc->SetPen(*wxBLACK_PEN);
// you would think the -1 offset here should be -2 to match the
// adjustment to rect.y (see above), but -1 produces correct output
AColor::Line(*dc, rect.x, rect.y + marg - 1, rect.x + rect.width, rect.y + marg - 1);
// since the margin gives us the bottom of the line,
// the extra -1 gets us to the top
AColor::Line(*dc, rect.x, rect.y + rect.height - marg - 1,
rect.x + rect.width, rect.y + rect.height - marg - 1);
}
}
void NoteTrackVRulerControls::UpdateRuler( const wxRect &rect )
{
// The note track isn't drawing a ruler at all!
// But it needs to!
const auto nt = std::static_pointer_cast< NoteTrack >( FindTrack() );
if (!nt)
return;
static Ruler ruler;
const auto vruler = &ruler;
vruler->SetBounds(rect.x, rect.y, rect.x + 1, rect.y + rect.height-1);
vruler->SetOrientation(wxVERTICAL);
vruler->GetMaxSize( &nt->vrulerSize.x, &nt->vrulerSize.y );
}
#endif

View File

@ -35,6 +35,14 @@ public:
AudacityProject *pProject) override;
private:
// TrackPanelDrawable implementation
void Draw(
TrackPanelDrawingContext &context,
const wxRect &rect, unsigned iPass ) override;
// TrackVRulerControls implementation
void UpdateRuler( const wxRect &rect ) override;
std::weak_ptr<NoteTrackVZoomHandle> mVZoomHandle;
};

View File

@ -13,6 +13,8 @@ Paul Licameli split from TrackPanel.cpp
#include "WaveTrackVZoomHandle.h"
#include "../../../../Experimental.h"
#include "../../../../HitTestResult.h"
#include "../../../../NumberScale.h"
#include "../../../../prefs/SpectrogramSettings.h"
@ -22,6 +24,12 @@ Paul Licameli split from TrackPanel.cpp
#include "../../../../TrackPanelMouseEvent.h"
#include "../../../../WaveTrack.h"
#include "../../../../AColor.h"
#include "../../../../AllThemeResources.h"
#include "../../../../TrackArtist.h"
#include "../../../../TrackPanelDrawingContext.h"
#include "../../../../widgets/Ruler.h"
///////////////////////////////////////////////////////////////////////////////
WaveTrackVRulerControls::~WaveTrackVRulerControls()
{
@ -198,3 +206,302 @@ unsigned WaveTrackVRulerControls::HandleWheelRotation
return RefreshCell | UpdateVRuler;
}
namespace {
Ruler &ruler()
{
static Ruler theRuler;
return theRuler;
}
}
void WaveTrackVRulerControls::Draw(
TrackPanelDrawingContext &context,
const wxRect &rect_, unsigned iPass )
{
TrackVRulerControls::Draw( context, rect_, iPass );
// Draw on a later pass because the bevel overpaints one pixel
// out of bounds on the bottom
if ( iPass == TrackArtist::PassControls ) {
auto rect = rect_;
--rect.width;
--rect.height;
auto dc = &context.dc;
// All waves have a ruler in the info panel
// The ruler needs a bevelled surround.
wxRect bev = rect;
bev.Inflate(-1, 0);
bev.width += 1;
bool highlight = false;
#ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
highlight = rect.Contains(context.lastState.GetPosition());
#endif
AColor::BevelTrackInfo(*dc, true, bev, highlight);
// Right align the ruler
wxRect rr = rect;
rr.width--;
auto t = FindTrack();
if ( !t )
return;
if ( t->vrulerSize.GetWidth() < rect.GetWidth()) {
int adj = rr.GetWidth() - t->vrulerSize.GetWidth();
rr.x += adj;
rr.width -= adj;
}
UpdateRuler(rr);
auto vruler = &ruler();
vruler->SetTickColour( theTheme.Colour( clrTrackPanelText ));
vruler->Draw(*dc);
}
}
void WaveTrackVRulerControls::UpdateRuler( const wxRect &rect )
{
const auto wt = std::static_pointer_cast< WaveTrack >( FindTrack() );
if (!wt)
return;
auto vruler = &ruler();
// All waves have a ruler in the info panel
// The ruler needs a bevelled surround.
const float dBRange =
wt->GetWaveformSettings().dBRange;
const int display = wt->GetDisplay();
if (display == WaveTrackViewConstants::Waveform) {
WaveformSettings::ScaleType scaleType =
wt->GetWaveformSettings().scaleType;
if (scaleType == WaveformSettings::stLinear) {
// Waveform
float min, max;
wt->GetDisplayBounds(&min, &max);
if (wt->GetLastScaleType() != scaleType &&
wt->GetLastScaleType() != -1)
{
// do a translation into the linear space
wt->SetLastScaleType();
wt->SetLastdBRange();
float sign = (min >= 0 ? 1 : -1);
if (min != 0.) {
min = DB_TO_LINEAR(fabs(min) * dBRange - dBRange);
if (min < 0.0)
min = 0.0;
min *= sign;
}
sign = (max >= 0 ? 1 : -1);
if (max != 0.) {
max = DB_TO_LINEAR(fabs(max) * dBRange - dBRange);
if (max < 0.0)
max = 0.0;
max *= sign;
}
wt->SetDisplayBounds(min, max);
}
vruler->SetDbMirrorValue( 0.0 );
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->SetUnits(wxT(""));
vruler->SetLabelEdges(false);
vruler->SetLog(false);
}
else {
wxASSERT(scaleType == WaveformSettings::stLogarithmic);
scaleType = WaveformSettings::stLogarithmic;
vruler->SetUnits(wxT(""));
float min, max;
wt->GetDisplayBounds(&min, &max);
float lastdBRange;
if (wt->GetLastScaleType() != scaleType &&
wt->GetLastScaleType() != -1)
{
// do a translation into the dB space
wt->SetLastScaleType();
wt->SetLastdBRange();
float sign = (min >= 0 ? 1 : -1);
if (min != 0.) {
min = (LINEAR_TO_DB(fabs(min)) + dBRange) / dBRange;
if (min < 0.0)
min = 0.0;
min *= sign;
}
sign = (max >= 0 ? 1 : -1);
if (max != 0.) {
max = (LINEAR_TO_DB(fabs(max)) + dBRange) / dBRange;
if (max < 0.0)
max = 0.0;
max *= sign;
}
wt->SetDisplayBounds(min, max);
}
else if (dBRange != (lastdBRange = wt->GetLastdBRange())) {
wt->SetLastdBRange();
// Remap the max of the scale
float newMax = max;
// This commented out code is problematic.
// min and max may be correct, and this code cause them to change.
#ifdef ONLY_LABEL_POSITIVE
const float sign = (max >= 0 ? 1 : -1);
if (max != 0.) {
// Ugh, duplicating from TrackPanel.cpp
#define ZOOMLIMIT 0.001f
const float extreme = LINEAR_TO_DB(2);
// recover dB value of max
const float dB = std::min(extreme, (float(fabs(max)) * lastdBRange - lastdBRange));
// find NEW scale position, but old max may get trimmed if the db limit rises
// Don't trim it to zero though, but leave max and limit distinct
newMax = sign * std::max(ZOOMLIMIT, (dBRange + dB) / dBRange);
// Adjust the min of the scale if we can,
// so the db Limit remains where it was on screen, but don't violate extremes
if (min != 0.)
min = std::max(-extreme, newMax * min / max);
}
#endif
wt->SetDisplayBounds(min, newMax);
}
// Old code was if ONLY_LABEL_POSITIVE were defined.
// it uses the +1 to 0 range only.
// the enabled code uses +1 to -1, and relies on set ticks labelling knowing about
// the dB scale.
#ifdef ONLY_LABEL_POSITIVE
if (max > 0) {
#endif
int top = 0;
float topval = 0;
int bot = rect.height;
float botval = -dBRange;
#ifdef ONLY_LABEL_POSITIVE
if (min < 0) {
bot = top + (int)((max / (max - min))*(bot - top));
min = 0;
}
if (max > 1) {
top += (int)((max - 1) / (max - min) * (bot - top));
max = 1;
}
if (max < 1 && max > 0)
topval = -((1 - max) * dBRange);
if (min > 0) {
botval = -((1 - min) * dBRange);
}
#else
topval = -((1 - max) * dBRange);
botval = -((1 - min) * dBRange);
vruler->SetDbMirrorValue( dBRange );
#endif
vruler->SetBounds(rect.x, rect.y + top, rect.x + rect.width, rect.y + bot - 1);
vruler->SetOrientation(wxVERTICAL);
vruler->SetRange(topval, botval);
#ifdef ONLY_LABEL_POSITIVE
}
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->SetLabelEdges(true);
vruler->SetLog(false);
}
}
else {
wxASSERT(display == WaveTrackViewConstants::Spectrum);
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
float minFreq, maxFreq;
wt->GetSpectrumBounds(&minFreq, &maxFreq);
vruler->SetDbMirrorValue( 0.0 );
switch (settings.scaleType) {
default:
wxASSERT(false);
case SpectrogramSettings::stLinear:
{
// Spectrum
if (rect.height < 60)
return;
/*
draw the ruler
we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
and append to the numbers a "k"
*/
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
vruler->SetOrientation(wxVERTICAL);
vruler->SetFormat(Ruler::RealFormat);
vruler->SetLabelEdges(true);
// use kHz in scale, if appropriate
if (maxFreq >= 2000) {
vruler->SetRange((maxFreq / 1000.), (minFreq / 1000.));
vruler->SetUnits(wxT("k"));
}
else {
// use Hz
vruler->SetRange((int)(maxFreq), (int)(minFreq));
vruler->SetUnits(wxT(""));
}
vruler->SetLog(false);
}
break;
case SpectrogramSettings::stLogarithmic:
case SpectrogramSettings::stMel:
case SpectrogramSettings::stBark:
case SpectrogramSettings::stErb:
case SpectrogramSettings::stPeriod:
{
// SpectrumLog
if (rect.height < 10)
return;
/*
draw the ruler
we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
and append to the numbers a "k"
*/
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
vruler->SetOrientation(wxVERTICAL);
vruler->SetFormat(Ruler::IntFormat);
vruler->SetLabelEdges(true);
vruler->SetRange(maxFreq, minFreq);
vruler->SetUnits(wxT(""));
vruler->SetLog(true);
NumberScale scale(
wt->GetSpectrogramSettings().GetScale( minFreq, maxFreq )
.Reversal() );
vruler->SetNumberScale(&scale);
}
break;
}
}
vruler->GetMaxSize( &wt->vrulerSize.x, &wt->vrulerSize.y );
}

View File

@ -35,6 +35,14 @@ public:
AudacityProject *pProject) override;
void DoZoomPreset( int i);
private:
// TrackPanelDrawable implementation
void Draw(
TrackPanelDrawingContext &context,
const wxRect &rect, unsigned iPass ) override;
// TrackVRulerControls implementation
void UpdateRuler( const wxRect &rect ) override;
std::weak_ptr<WaveTrackVZoomHandle> mVZoomHandle;
};

View File

@ -13,6 +13,86 @@ Paul Licameli split from TrackPanel.cpp
#include "../../../HitTestResult.h"
#include "../../../AColor.h"
#include "../../../AllThemeResources.h"
#include "../../../TimeTrack.h"
#include "../../../TrackArtist.h"
#include "../../../TrackPanelDrawingContext.h"
#include "../../../widgets/Ruler.h"
TimeTrackVRulerControls::~TimeTrackVRulerControls()
{
}
namespace {
Ruler &ruler()
{
static Ruler theRuler;
return theRuler;
}
}
void TimeTrackVRulerControls::Draw(
TrackPanelDrawingContext &context,
const wxRect &rect_, unsigned iPass )
{
TrackVRulerControls::Draw( context, rect_, iPass );
// Draw on a later pass because the bevel overpaints one pixel
// out of bounds on the bottom
if ( iPass == TrackArtist::PassControls ) {
auto t = FindTrack();
if ( !t )
return;
auto rect = rect_;
--rect.width;
--rect.height;
auto dc = &context.dc;
wxRect bev = rect;
bev.Inflate(-1, 0);
bev.width += 1;
AColor::BevelTrackInfo(*dc, true, bev);
// Right align the ruler
wxRect rr = rect;
rr.width--;
if (t && t->vrulerSize.GetWidth() < rect.GetWidth()) {
int adj = rr.GetWidth() - t->vrulerSize.GetWidth();
rr.x += adj;
rr.width -= adj;
}
UpdateRuler(rr);
auto vruler = &ruler();
vruler->SetTickColour( theTheme.Colour( clrTrackPanelText ));
vruler->Draw(*dc);
}
}
void TimeTrackVRulerControls::UpdateRuler( const wxRect &rect )
{
const auto tt = std::static_pointer_cast< TimeTrack >( FindTrack() );
if (!tt)
return;
auto vruler = &ruler();
float min, max;
min = tt->GetRangeLower() * 100.0;
max = tt->GetRangeUpper() * 100.0;
vruler->SetDbMirrorValue( 0.0 );
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->SetUnits(wxT(""));
vruler->SetLabelEdges(false);
vruler->SetLog(tt->GetDisplayLog());
vruler->GetMaxSize( &tt->vrulerSize.x, &tt->vrulerSize.y );
}

View File

@ -25,6 +25,17 @@ public:
TimeTrackVRulerControls( const std::shared_ptr<TrackView> &pTrackView )
: TrackVRulerControls( pTrackView ) {}
~TimeTrackVRulerControls();
private:
// TrackPanelDrawable implementation
void Draw(
TrackPanelDrawingContext &context,
const wxRect &rect, unsigned iPass ) override;
// TrackVRulerControls implementation
void UpdateRuler( const wxRect &rect ) override;
};
#endif

View File

@ -114,3 +114,14 @@ void TrackVRulerControls::Draw(
}
}
}
wxRect TrackVRulerControls::DrawingArea(
const wxRect &rect, const wxRect &, unsigned iPass )
{
// Common area change for all subclasses when drawing the controls
// A bevel extends below one pixel outside of the hit-test area
if ( iPass == TrackArtist::PassControls )
return { rect.x, rect.y, rect.width, rect.height + 1 };
else
return rect;
}

View File

@ -43,6 +43,10 @@ public:
( wxDC *dc, const wxRect &cellRect, const wxRect &panelRect,
int zoomStart, int zoomEnd);
// Modify the ruler rectangle, and related display parameters,
// cached in the associated track
virtual void UpdateRuler( const wxRect &rect ) = 0;
protected:
std::shared_ptr<Track> DoFindTrack() override;
@ -51,6 +55,9 @@ protected:
TrackPanelDrawingContext &context,
const wxRect &rect, unsigned iPass ) override;
wxRect DrawingArea(
const wxRect &rect, const wxRect &panelRect, unsigned iPass ) override;
Track *GetTrack() const;
std::weak_ptr<TrackView> mwTrackView;