Move code for vertical ruler clicks, drags, popup menus, scroll wheel

This commit is contained in:
Paul Licameli 2015-08-04 12:08:25 -04:00 committed by Paul Licameli
parent a313bcdb11
commit 91e6239eeb
8 changed files with 1007 additions and 761 deletions

View File

@ -185,7 +185,6 @@ is time to refresh some aspect of the screen.
#include "ondemand/ODManager.h"
#include "prefs/SpectrogramSettings.h"
#include "prefs/WaveformSettings.h"
#include "toolbars/ControlToolBar.h"
#include "toolbars/ToolsToolBar.h"
@ -193,8 +192,6 @@ is time to refresh some aspect of the screen.
// To do: eliminate this!
#include "tracks/ui/Scrubbing.h"
#define ZOOMLIMIT 0.001f
//This loads the appropriate set of cursors, depending on platform.
#include "../images/Cursors.h"
@ -282,18 +279,6 @@ template < class CLIPPEE, class CLIPVAL >
enum {
TrackPanelFirstID = 2000,
// Reserve an ample block of ids for waveform scale types
OnFirstWaveformScaleID,
OnLastWaveformScaleID = OnFirstWaveformScaleID + 9,
// Reserve an ample block of ids for spectrum scale types
OnFirstSpectrumScaleID,
OnLastSpectrumScaleID = OnFirstSpectrumScaleID + 19,
OnZoomInVerticalID,
OnZoomOutVerticalID,
OnZoomFitVerticalID,
};
BEGIN_EVENT_TABLE(TrackPanel, OverlayPanel)
@ -308,13 +293,6 @@ BEGIN_EVENT_TABLE(TrackPanel, OverlayPanel)
EVT_KILL_FOCUS(TrackPanel::OnKillFocus)
EVT_CONTEXT_MENU(TrackPanel::OnContextMenu)
EVT_MENU_RANGE(OnFirstWaveformScaleID, OnLastWaveformScaleID, TrackPanel::OnWaveformScaleType)
EVT_MENU_RANGE(OnFirstSpectrumScaleID, OnLastSpectrumScaleID, TrackPanel::OnSpectrumScaleType)
EVT_MENU(OnZoomInVerticalID, TrackPanel::OnZoomInVertical)
EVT_MENU(OnZoomOutVerticalID, TrackPanel::OnZoomOutVertical)
EVT_MENU(OnZoomFitVerticalID, TrackPanel::OnZoomFitVertical)
EVT_TIMER(wxID_ANY, TrackPanel::OnTimer)
END_EVENT_TABLE()
@ -389,8 +367,6 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id,
mSelectCursor = MakeCursor( wxCURSOR_IBEAM, IBeamCursorXpm, 17, 16);
mEnvelopeCursor= MakeCursor( wxCURSOR_ARROW, EnvCursorXpm, 16, 16);
mDisabledCursor= MakeCursor( wxCURSOR_NO_ENTRY, DisabledCursorXpm,16, 16);
mZoomInCursor = MakeCursor( wxCURSOR_MAGNIFIER, ZoomInCursorXpm, 19, 15);
mZoomOutCursor = MakeCursor( wxCURSOR_MAGNIFIER, ZoomOutCursorXpm, 19, 15);
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
mBottomFrequencyCursor =
@ -426,9 +402,6 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id,
// Timer is started after the window is visible
GetProject()->Bind(wxEVT_IDLE, &TrackPanel::OnIdle, this);
mZoomStart = -1;
mZoomEnd = -1;
// This is used to snap the cursor to the nearest track that
// lines up with it.
mSnapManager = NULL;
@ -510,40 +483,10 @@ void TrackPanel::BuildMenus(void)
{
// Get rid of existing menus
DeleteMenus();
/*
mRulerWaveformMenu = std::make_unique<wxMenu>();
BuildVRulerMenuItems
(mRulerWaveformMenu.get(), OnFirstWaveformScaleID,
WaveformSettings::GetScaleNames());
mRulerSpectrumMenu = std::make_unique<wxMenu>();
BuildVRulerMenuItems
(mRulerSpectrumMenu.get(), OnFirstSpectrumScaleID,
SpectrogramSettings::GetScaleNames());
*/
}
/*
// left over from PRL's vertical ruler context menu experiment in 2.1.2
// static
void TrackPanel::BuildVRulerMenuItems
(wxMenu * menu, int firstId, const wxArrayString &names)
{
int id = firstId;
for (int ii = 0, nn = names.size(); ii < nn; ++ii)
menu->AppendRadioItem(id++, names[ii]);
menu->AppendSeparator();
menu->Append(OnZoomInVerticalID, _("Zoom In\tLeft-Click/Left-Drag"));
menu->Append(OnZoomOutVerticalID, _("Zoom Out\tShift-Left-Click"));
menu->Append(OnZoomFitVerticalID, _("Zoom to Fit\tShift-Right-Click"));
}
*/
void TrackPanel::DeleteMenus(void)
{
mRulerWaveformMenu.reset();
mRulerSpectrumMenu.reset();
}
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
@ -933,7 +876,6 @@ void TrackPanel::HandleInterruptedDrag()
else switch (mMouseCapture)
{
case IsUncaptured:
case IsVZooming:
case IsSelecting:
case IsSelectingLabelText:
case IsResizing:
@ -1082,8 +1024,6 @@ bool TrackPanel::HandleEscapeKey(bool down)
pMixerBoard->Refresh();
}
break;
case IsVZooming:
break;
case IsResizing:
mCapturedTrack->SetHeight(mInitialActualHeight);
mCapturedTrack->SetMinimized(mInitialMinimized);
@ -1230,28 +1170,10 @@ bool TrackPanel::SetCursorByActivity( )
/// When in the "label" (TrackInfo or vertical ruler), we can either vertical zoom or re-order tracks.
/// Dont't change cursor/tip to zoom if display is not waveform (either linear of dB) or Spectrum
void TrackPanel::SetCursorAndTipWhenInLabel( Track * t,
const wxMouseEvent &event, wxString &tip )
void TrackPanel::SetCursorAndTipWhenInLabel( Track * ,
const wxMouseEvent &, wxString &tip )
{
if (event.m_x >= GetVRulerOffset() && (t->GetKind() == Track::Wave) )
{
tip = _("Click to vertically zoom in. Shift-click to zoom out. Drag to specify a zoom region.");
SetCursor(event.ShiftDown()? *mZoomOutCursor : *mZoomInCursor);
}
#ifdef USE_MIDI
else if (event.m_x >= GetVRulerOffset() && t->GetKind() == Track::Note) {
tip = _("Click to verticaly zoom in, Shift-click to zoom out, Drag to create a particular zoom region.");
SetCursor(event.ShiftDown() ? *mZoomOutCursor : *mZoomInCursor);
}
#endif
else if (event.m_x >= GetVRulerOffset() ){
// In VRuler but probably in a label track, and clicks don't do anything here, so no tip.
// Use a space for the tip, otherwsie we get he default message.
// TODO: Maybe the code for label tracks SHOULD treat the VRuler as part of the TrackInfo?
tip = wxT(" ");
SetCursor( *mArrowCursor );
}
else if( GetTrackCount() > 1 ){
if( GetTrackCount() > 1 ) {
// Set a status message if over TrackInfo.
//tip = _("Drag the track vertically to change the order of the tracks.");
// i18n-hint: %s is replaced by (translation of) 'Ctrl-Click' on windows, 'Command-Click' on Mac
@ -2907,12 +2829,6 @@ bool mayDragWidth, bool onlyWithinSnapDistance,
}
}
/// Determines if drag zooming is active
bool TrackPanel::IsDragZooming(int zoomStart, int zoomEnd)
{
return (abs(zoomEnd - zoomStart) > DragThreshold);
}
/// Determines if the a modal tool is active
bool TrackPanel::IsMouseCaptured()
{
@ -2920,348 +2836,6 @@ bool TrackPanel::IsMouseCaptured()
|| mUIHandle != NULL);
}
/// Vertical zooming (triggered by clicking in the
/// vertical ruler)
void TrackPanel::HandleVZoom(wxMouseEvent & event)
{
if (event.ButtonDown() || event.ButtonDClick()) {
HandleVZoomClick( event );
}
else if (event.Dragging()) {
HandleVZoomDrag( event );
}
else if (event.ButtonUp()) {
HandleVZoomButtonUp( event );
}
//TODO-MB: add timetrack zooming here!
}
/// VZoom click
void TrackPanel::HandleVZoomClick( wxMouseEvent & event )
{
if (mCapturedTrack)
return;
const auto foundCell = FindCell(event.m_x, event.m_y);
mCapturedTrack = foundCell.pTrack;
mCapturedRect = foundCell.rect;
if (foundCell.type != CellType::VRuler || !(mCapturedTrack = foundCell.pTrack))
return;
if (mCapturedTrack->GetKind() == Track::Wave
#ifdef USE_MIDI
|| mCapturedTrack->GetKind() == Track::Note
#endif
)
{
mMouseCapture = IsVZooming;
mZoomStart = event.m_y;
mZoomEnd = event.m_y;
// change note track to zoom like audio track
//#ifdef USE_MIDI
// if (mCapturedTrack->GetKind() == Track::Note) {
// ((NoteTrack *) mCapturedTrack)->StartVScroll();
// }
//#endif
}
}
/// VZoom drag
void TrackPanel::HandleVZoomDrag( wxMouseEvent & event )
{
mZoomEnd = event.m_y;
if (IsDragZooming()){
// changed Note track to work like audio track
//#ifdef USE_MIDI
// if (mCapturedTrack && mCapturedTrack->GetKind() == Track::Note) {
// ((NoteTrack *) mCapturedTrack)->VScroll(mZoomStart, mZoomEnd);
// }
//#endif
Refresh(false);
}
}
/// VZoom Button up.
/// There are three cases:
/// - Drag-zooming; we already have a min and max
/// - Zoom out; ensure we don't go too small.
/// - Zoom in; ensure we don't go too large.
void TrackPanel::HandleVZoomButtonUp( wxMouseEvent & event )
{
if (!mCapturedTrack)
return;
mMouseCapture = IsUncaptured;
#ifdef USE_MIDI
// handle vertical scrolling in Note Track. This is so different from
// zooming in audio tracks that it is handled as a special case from
// which we then return
if (mCapturedTrack->GetKind() == Track::Note) {
NoteTrack *nt = (NoteTrack *) mCapturedTrack;
const wxRect rect{
GetLeftOffset(),
mCapturedTrack->GetY() + kTopMargin,
GetSize().GetWidth() - GetLeftOffset(),
mCapturedTrack->GetHeight() - (kTopMargin + kBottomMargin)
};
if (IsDragZooming()) {
nt->ZoomTo(rect, mZoomStart, mZoomEnd);
} else if (event.ShiftDown() || event.RightUp()) {
nt->ZoomOut(rect, mZoomEnd);
} else {
nt->ZoomIn(rect, mZoomEnd);
}
mZoomEnd = mZoomStart = 0;
Refresh(false);
mCapturedTrack = NULL;
MakeParentModifyState(true);
return;
}
#endif
// don't do anything if track is not wave
if (mCapturedTrack->GetKind() != Track::Wave)
return;
/*
if (event.RightUp() &&
!(event.ShiftDown() || event.CmdDown())) {
OnVRulerMenu(mCapturedTrack, &event);
return;
}
*/
HandleWaveTrackVZoom(static_cast<WaveTrack*>(mCapturedTrack),
event.ShiftDown(), event.RightUp());
mCapturedTrack = NULL;
}
void TrackPanel::HandleWaveTrackVZoom(WaveTrack *track, bool shiftDown, bool rightUp)
{
HandleWaveTrackVZoom(GetTracks(), mCapturedRect, mZoomStart, mZoomEnd,
track, shiftDown, rightUp, false);
mZoomEnd = mZoomStart = 0;
UpdateVRuler(track);
Refresh(false);
MakeParentModifyState(true);
}
//static
void TrackPanel::HandleWaveTrackVZoom
(TrackList *tracks, const wxRect &rect,
int zoomStart, int zoomEnd,
WaveTrack *track, bool shiftDown, bool rightUp,
bool fixedMousePoint)
{
// Assume linked track is wave or null
const auto partner = static_cast<WaveTrack *>(track->GetLink());
int height = track->GetHeight() - (kTopMargin + kBottomMargin);
int ypos = rect.y;
// Ensure start and end are in order (swap if not).
if (zoomEnd < zoomStart)
std::swap(zoomStart, zoomEnd);
float min, max, minBand = 0;
const double rate = track->GetRate();
const float halfrate = rate / 2;
const SpectrogramSettings &settings = track->GetSpectrogramSettings();
NumberScale scale;
const bool spectral = (track->GetDisplay() == WaveTrack::Spectrum);
const bool spectrumLinear = spectral &&
(track->GetSpectrogramSettings().scaleType == SpectrogramSettings::stLinear);
if (spectral) {
track->GetSpectrumBounds(&min, &max);
scale = settings.GetScale( min, max );
const auto fftLength = settings.GetFFTLength();
const float binSize = rate / fftLength;
// JKC: Following discussions of Bug 1208 I'm allowing zooming in
// down to one bin.
// const int minBins =
// std::min(10, fftLength / 2); //minimum 10 freq bins, unless there are less
const int minBins = 1;
minBand = minBins * binSize;
}
else
track->GetDisplayBounds(&min, &max);
if (IsDragZooming(zoomStart, zoomEnd)) {
// Drag Zoom
const float tmin = min, tmax = max;
if (spectral) {
double xmin = 1 - (zoomEnd - ypos) / (float)height;
double xmax = 1 - (zoomStart - ypos) / (float)height;
const float middle = (xmin + xmax) / 2;
const float middleValue = scale.PositionToValue(middle);
min = std::max(spectrumLinear ? 0.0f : 1.0f,
std::min(middleValue - minBand / 2,
scale.PositionToValue(xmin)
));
max = std::min(halfrate,
std::max(middleValue + minBand / 2,
scale.PositionToValue(xmax)
));
}
else {
const float p1 = (zoomStart - ypos) / (float)height;
const float p2 = (zoomEnd - ypos) / (float)height;
max = (tmax * (1.0-p1) + tmin * p1);
min = (tmax * (1.0-p2) + tmin * p2);
// Waveform view - allow zooming down to a range of ZOOMLIMIT
if (max - min < ZOOMLIMIT) { // if user attempts to go smaller...
const float c = (min+max)/2; // ...set centre of view to centre of dragged area and top/bottom to ZOOMLIMIT/2 above/below
min = c - ZOOMLIMIT/2.0;
max = c + ZOOMLIMIT/2.0;
}
}
}
else if (shiftDown || rightUp) {
// Zoom OUT
if (spectral) {
if (shiftDown && rightUp) {
// Zoom out full
min = spectrumLinear ? 0.0f : 1.0f;
max = halfrate;
}
else {
// Zoom out
const float p1 = (zoomStart - ypos) / (float)height;
// (Used to zoom out centered at midline, ignoring the click, if linear view.
// I think it is better to be consistent. PRL)
// Center zoom-out at the midline
const float middle = // spectrumLinear ? 0.5f :
1.0f - p1;
if (fixedMousePoint) {
min = std::max(spectrumLinear ? 0.0f : 1.0f, scale.PositionToValue(-middle));
max = std::min(halfrate, scale.PositionToValue(1.0f + p1));
}
else {
min = std::max(spectrumLinear ? 0.0f : 1.0f, scale.PositionToValue(middle - 1.0f));
max = std::min(halfrate, scale.PositionToValue(middle + 1.0f));
}
}
}
else {
// Zoom out to -1.0...1.0 first, then, and only
// then, if they click again, allow one more
// zoom out.
if (shiftDown && rightUp) {
// Zoom out full
min = -1.0;
max = 1.0;
}
else {
// Zoom out
const WaveformSettings &settings = track->GetWaveformSettings();
const bool linear = settings.isLinear();
const float top = linear
? 2.0
: (LINEAR_TO_DB(2.0) + settings.dBRange) / settings.dBRange;
if (min <= -1.0 && max >= 1.0) {
// Go to the maximal zoom-out
min = -top;
max = top;
}
else {
// limit to +/- 1 range unless already outside that range...
float minRange = (min < -1) ? -top : -1.0;
float maxRange = (max > 1) ? top : 1.0;
// and enforce vertical zoom limits.
const float p1 = (zoomStart - ypos) / (float)height;
if (fixedMousePoint) {
const float oldRange = max - min;
const float c = (max * (1.0 - p1) + min * p1);
min = std::min(maxRange - ZOOMLIMIT,
std::max(minRange, c - 2 * (1.0f - p1) * oldRange));
max = std::max(minRange + ZOOMLIMIT,
std::min(maxRange, c + 2 * p1 * oldRange));
}
else {
const float c = p1 * min + (1 - p1) * max;
const float l = (max - min);
min = std::min(maxRange - ZOOMLIMIT,
std::max(minRange, c - l));
max = std::max(minRange + ZOOMLIMIT,
std::min(maxRange, c + l));
}
}
}
}
}
else {
// Zoom IN
if (spectral) {
// Center the zoom-in at the click
const float p1 = (zoomStart - ypos) / (float)height;
const float middle = 1.0f - p1;
const float middleValue = scale.PositionToValue(middle);
if (fixedMousePoint) {
min = std::max(spectrumLinear ? 0.0f : 1.0f,
std::min(middleValue - minBand * middle,
scale.PositionToValue(0.5f * middle)
));
max = std::min(halfrate,
std::max(middleValue + minBand * p1,
scale.PositionToValue(middle + 0.5f * p1)
));
}
else {
min = std::max(spectrumLinear ? 0.0f : 1.0f,
std::min(middleValue - minBand / 2,
scale.PositionToValue(middle - 0.25f)
));
max = std::min(halfrate,
std::max(middleValue + minBand / 2,
scale.PositionToValue(middle + 0.25f)
));
}
}
else {
// Zoom in centered on cursor
if (min < -1.0 || max > 1.0) {
min = -1.0;
max = 1.0;
}
else {
// Enforce maximum vertical zoom
const float oldRange = max - min;
const float l = std::max(ZOOMLIMIT, 0.5f * oldRange);
const float ratio = l / (max - min);
const float p1 = (zoomStart - ypos) / (float)height;
const float c = (max * (1.0 - p1) + min * p1);
if (fixedMousePoint)
min = c - ratio * (1.0f - p1) * oldRange,
max = c + ratio * p1 * oldRange;
else
min = c - 0.5 * l,
max = c + 0.5 * l;
}
}
}
if (spectral) {
track->SetSpectrumBounds(min, max);
if (partner)
partner->SetSpectrumBounds(min, max);
}
else {
track->SetDisplayBounds(min, max);
if (partner)
partner->SetDisplayBounds(min, max);
}
}
void TrackPanel::UpdateViewIfNoTracks()
{
if (mTracks->IsEmpty())
@ -3986,20 +3560,6 @@ void TrackPanel::HandleWheelRotation(wxMouseEvent & event)
// you do see changes in the time ruler.
return;
// Special case of pointer in the vertical ruler
if (event.ShiftDown() || event.CmdDown()) {
const auto foundCell = FindCell(event.m_x, event.m_y);
auto &pTrack = foundCell.pTrack;
auto &rect = foundCell.rect;
if (pTrack && foundCell.type == CellType::VRuler) {
HandleWheelRotationInVRuler(event, steps, pTrack, rect);
// Always stop propagation even if the ruler didn't change. The ruler
// is a narrow enough target.
event.Skip(false);
return;
}
}
if (event.ShiftDown()
// Don't pan during smooth scrolling. That would conflict with keeping
// the play indicator centered.
@ -4083,128 +3643,6 @@ void TrackPanel::HandleWheelRotation(wxMouseEvent & event)
}
}
void TrackPanel::HandleWheelRotationInVRuler
(wxMouseEvent &event, double steps, Track *pTrack, const wxRect &rect)
{
if (pTrack->GetKind() == Track::Wave) {
WaveTrack *const wt = static_cast<WaveTrack*>(pTrack);
// Assume linked track is wave or null
const auto partner = static_cast<WaveTrack*>(wt->GetLink());
const bool isDB =
wt->GetDisplay() == WaveTrack::Waveform &&
wt->GetWaveformSettings().scaleType == WaveformSettings::stLogarithmic;
// Special cases for Waveform dB only.
// Set the bottom of the dB scale but only if it's visible
if (isDB && event.ShiftDown() && event.CmdDown()) {
float min, max;
wt->GetDisplayBounds(&min, &max);
if (min < 0.0 && max > 0.0) {
WaveformSettings &settings = wt->GetIndependentWaveformSettings();
float olddBRange = settings.dBRange;
if (steps < 0)
// Zoom out
settings.NextLowerDBRange();
else
settings.NextHigherDBRange();
float newdBRange = settings.dBRange;
if (partner) {
WaveformSettings &settings = partner->GetIndependentWaveformSettings();
if (steps < 0)
// Zoom out
settings.NextLowerDBRange();
else
settings.NextHigherDBRange();
}
// Is y coordinate within the rectangle half-height centered about
// the zero level?
const auto zeroLevel = wt->ZeroLevelYCoordinate(rect);
const bool fixedMagnification =
(4 * std::abs(event.GetY() - zeroLevel) < rect.GetHeight());
if (fixedMagnification) {
// Vary the db limit without changing
// magnification; that is, peaks and troughs move up and down
// rigidly, as parts of the wave near zero are exposed or hidden.
const float extreme = (LINEAR_TO_DB(2) + newdBRange) / newdBRange;
max = std::min(extreme, max * olddBRange / newdBRange);
min = std::max(-extreme, min * olddBRange / newdBRange);
wt->SetLastdBRange();
wt->SetDisplayBounds(min, max);
if (partner) {
partner->SetLastdBRange();
partner->SetDisplayBounds(min, max);
}
}
}
}
else if (event.CmdDown() && !event.ShiftDown()) {
HandleWaveTrackVZoom(
GetTracks(), rect, event.m_y, event.m_y,
wt, false, (steps < 0),
true);
}
else if (!event.CmdDown() && event.ShiftDown()) {
// Scroll some fixed number of pixels, independent of zoom level or track height:
static const float movement = 10.0f;
const int height = wt->GetHeight() - (kTopMargin + kBottomMargin);
const bool spectral = (wt->GetDisplay() == WaveTrack::Spectrum);
if (spectral) {
const float delta = steps * movement / height;
SpectrogramSettings &settings = wt->GetIndependentSpectrogramSettings();
const bool isLinear = settings.scaleType == SpectrogramSettings::stLinear;
float bottom, top;
wt->GetSpectrumBounds(&bottom, &top);
const double rate = wt->GetRate();
const float bound = rate / 2;
const NumberScale numberScale( settings.GetScale( bottom, top ) );
float newTop =
std::min(bound, numberScale.PositionToValue(1.0f + delta));
const float newBottom =
std::max((isLinear ? 0.0f : 1.0f),
numberScale.PositionToValue(numberScale.ValueToPosition(newTop) - 1.0f));
newTop =
std::min(bound,
numberScale.PositionToValue(numberScale.ValueToPosition(newBottom) + 1.0f));
wt->SetSpectrumBounds(newBottom, newTop);
if (partner)
partner->SetSpectrumBounds(newBottom, newTop);
}
else {
float topLimit = 2.0;
if (isDB) {
const float dBRange = wt->GetWaveformSettings().dBRange;
topLimit = (LINEAR_TO_DB(topLimit) + dBRange) / dBRange;
}
const float bottomLimit = -topLimit;
float top, bottom;
wt->GetDisplayBounds(&bottom, &top);
const float range = top - bottom;
const float delta = range * steps * movement / height;
float newTop = std::min(topLimit, top + delta);
const float newBottom = std::max(bottomLimit, newTop - range);
newTop = std::min(topLimit, newBottom + range);
wt->SetDisplayBounds(newBottom, newTop);
if (partner)
partner->SetDisplayBounds(newBottom, newTop);
}
}
else
return;
UpdateVRuler(pTrack);
Refresh(false);
MakeParentModifyState(true);
}
else {
// To do: time track? Note track?
}
return;
}
/// Filter captured keys typed into LabelTracks.
void TrackPanel::OnCaptureKey(wxCommandEvent & event)
{
@ -4517,9 +3955,6 @@ try
}
}
else switch( mMouseCapture ) {
case IsVZooming:
HandleVZoom(event);
break;
case IsResizing:
case IsResizingBetweenLinkedTracks:
case IsResizingBelowLinkedTracks:
@ -4794,8 +4229,6 @@ void TrackPanel::HandleTrackSpecificMouseEvent(wxMouseEvent & event)
else {
if (foundCell.type == CellType::VRuler) {
if (!event.Dragging()) // JKC: Only want the mouse down event.
HandleVZoom(event);
}
else if (foundCell.type == CellType::Label)
HandleLabelClick(event);
@ -5139,15 +4572,6 @@ void TrackPanel::DrawEverythingElse(wxDC * dc,
if (mUIHandle)
mUIHandle->DrawExtras(UIHandle::Cells, dc, region, clip);
if (mMouseCapture == IsVZooming && IsDragZooming()
// note track zooming now works like audio track
//#ifdef USE_MIDI
// && mCapturedTrack && mCapturedTrack->GetKind() != Track::Note
//#endif
) {
DrawZooming(dc, clip);
}
// Paint over the part below the tracks
trackRect.y += trackRect.height;
if (trackRect.y < clip.GetBottom()) {
@ -5176,35 +4600,6 @@ void TrackPanel::DrawEverythingElse(wxDC * dc,
mSnapManager->Draw( dc, GetSnapLeft(), GetSnapRight() );
}
/// Draw zooming indicator that shows the region that will
/// be zoomed into when the user clicks and drags with a
/// zoom cursor. Handles both vertical and horizontal
/// zooming.
void TrackPanel::DrawZooming(wxDC * dc, const wxRect & clip)
{
wxRect rect;
dc->SetBrush(*wxTRANSPARENT_BRUSH);
dc->SetPen(*wxBLACK_DASHED_PEN);
if (mMouseCapture==IsVZooming) {
rect.y = std::min(mZoomStart, mZoomEnd);
rect.height = 1 + abs(mZoomEnd - mZoomStart);
rect.x = GetVRulerOffset();
rect.SetRight(GetSize().x - kRightMargin); // extends into border rect
}
else {
rect.x = std::min(mZoomStart, mZoomEnd);
rect.width = 1 + abs(mZoomEnd - mZoomStart);
rect.y = -1;
rect.height = clip.height + 2;
}
dc->DrawRectangle(rect);
}
// Make this #include go away!
#include "tracks/ui/TrackControls.h"
@ -5806,56 +5201,6 @@ void TrackPanel::OnTrackMenu(Track *t)
Refresh(false);
}
void TrackPanel::OnVRulerMenu(Track *t, wxMouseEvent *pEvent)
{
if (!t) {
t = GetFocusedTrack();
if (!t)
return;
}
if (t->GetKind() != Track::Wave)
return;
WaveTrack *const wt = static_cast<WaveTrack*>(t);
const int display = wt->GetDisplay();
wxMenu *theMenu;
if (display == WaveTrack::Waveform) {
theMenu = mRulerWaveformMenu.get();
const int id =
OnFirstWaveformScaleID + (int)(wt->GetWaveformSettings().scaleType);
theMenu->Check(id, true);
}
else {
theMenu = mRulerSpectrumMenu.get();
const int id =
OnFirstSpectrumScaleID + (int)(wt->GetSpectrogramSettings().scaleType);
theMenu->Check(id, true);
}
int x, y;
if (pEvent)
x = pEvent->m_x, y = pEvent->m_y;
else {
// If no event given, pop up the menu at the same height
// as for the track control menu
const wxRect rect = FindTrackRect(wt, true);
wxRect titleRect;
mTrackInfo.GetTitleBarRect(rect, titleRect);
x = GetVRulerOffset(), y = titleRect.y + titleRect.height + 1;
}
// So that IsDragZooming() returns false, and if we zoom in, we do so
// centered where the mouse is now:
mZoomStart = mZoomEnd = pEvent->m_y;
mPopupMenuTarget = wt;
PopupMenu(theMenu, x, y);
mPopupMenuTarget = NULL;
}
Track * TrackPanel::GetFirstSelectedTrack()
{
@ -5994,68 +5339,6 @@ void TrackPanel::DrawShadow(Track * /* t */ , wxDC * dc, const wxRect & rect)
AColor::Line(*dc, right, rect.y, right, rect.y + 1);
}
void TrackPanel::OnWaveformScaleType(wxCommandEvent &evt)
{
// Get here only from vertical ruler menu for wave tracks
const auto wt = static_cast<WaveTrack *>(mPopupMenuTarget);
// Assume linked track is wave or null
const auto partner = static_cast<WaveTrack*>(wt->GetLink());
const WaveformSettings::ScaleType newScaleType =
WaveformSettings::ScaleType(
std::max(0,
std::min((int)(WaveformSettings::stNumScaleTypes) - 1,
evt.GetId() - OnFirstWaveformScaleID
)));
if (wt->GetWaveformSettings().scaleType != newScaleType) {
wt->GetIndependentWaveformSettings().scaleType = newScaleType;
if (partner)
partner->GetIndependentWaveformSettings().scaleType = newScaleType;
UpdateVRuler(wt); // Is this really needed?
MakeParentModifyState(true);
Refresh(false);
}
}
void TrackPanel::OnSpectrumScaleType(wxCommandEvent &evt)
{
// Get here only from vertical ruler menu for wave tracks
const auto wt = static_cast<WaveTrack *>(mPopupMenuTarget);
// Assume linked track is wave or null
const auto partner = static_cast<WaveTrack*>(wt->GetLink());
const SpectrogramSettings::ScaleType newScaleType =
SpectrogramSettings::ScaleType(
std::max(0,
std::min((int)(SpectrogramSettings::stNumScaleTypes) - 1,
evt.GetId() - OnFirstSpectrumScaleID
)));
if (wt->GetSpectrogramSettings().scaleType != newScaleType) {
wt->GetIndependentSpectrogramSettings().scaleType = newScaleType;
if (partner)
partner->GetIndependentSpectrogramSettings().scaleType = newScaleType;
UpdateVRuler(wt); // Is this really needed?
MakeParentModifyState(true);
Refresh(false);
}
}
void TrackPanel::OnZoomInVertical(wxCommandEvent &)
{
// Get here only from vertical ruler menu for wave tracks
HandleWaveTrackVZoom(static_cast<WaveTrack*>(mPopupMenuTarget), false, false);
}
void TrackPanel::OnZoomOutVertical(wxCommandEvent &)
{
// Get here only from vertical ruler menu for wave tracks
HandleWaveTrackVZoom(static_cast<WaveTrack*>(mPopupMenuTarget), true, false);
}
void TrackPanel::OnZoomFitVertical(wxCommandEvent &)
{
// Get here only from vertical ruler menu for wave tracks
HandleWaveTrackVZoom(static_cast<WaveTrack*>(mPopupMenuTarget), true, true);
}
/// Determines which cell is under the mouse
/// @param mouseX - mouse X position.
/// @param mouseY - mouse Y position.

View File

@ -317,7 +317,6 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
virtual void ScrollIntoView(int x);
virtual void OnTrackMenu(Track *t = NULL);
virtual void OnVRulerMenu(Track *t, wxMouseEvent *pEvent = NULL);
virtual Track * GetFirstSelectedTrack();
virtual bool IsMouseCaptured();
@ -340,9 +339,6 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
protected:
virtual MixerBoard* GetMixerBoard();
// left over from PRL's vertical ruler context menu experiment in 2.1.2
// static void BuildVRulerMenuItems(wxMenu * menu, int firstId, const wxArrayString &names);
virtual bool IsAudioActive();
virtual bool IsUnsafe();
virtual bool HandleLabelTrackClick(LabelTrack * lTrack, const wxRect &rect, wxMouseEvent & event);
@ -447,24 +443,8 @@ public:
protected:
virtual void MaySetOnDemandTip( Track * t, wxString &tip );
static bool IsDragZooming(int zoomStart, int zoomEnd);
virtual bool IsDragZooming() { return IsDragZooming(mZoomStart, mZoomEnd); }
virtual void HandleVZoom(wxMouseEvent & event);
virtual void HandleVZoomClick(wxMouseEvent & event);
virtual void HandleVZoomDrag(wxMouseEvent & event);
virtual void HandleVZoomButtonUp(wxMouseEvent & event);
virtual void HandleWaveTrackVZoom(WaveTrack *track, bool shiftDown, bool rightUp);
static void HandleWaveTrackVZoom
(TrackList *tracks, const wxRect &rect,
int zoomStart, int zoomEnd,
WaveTrack *track, bool shiftDown, bool rightUp,
bool fixedMousePoint);
// MM: Handle mouse wheel rotation
virtual void HandleWheelRotation(wxMouseEvent & event);
virtual void HandleWheelRotationInVRuler
(wxMouseEvent &event, double steps, Track *pTrack, const wxRect &rect);
// Handle resizing.
virtual void HandleResizeClick(wxMouseEvent & event);
@ -488,13 +468,6 @@ protected:
virtual void MakeParentModifyState(bool bWantsAutoSave); // if true, writes auto-save file. Should set only if you really want the state change restored after
// a crash, as it can take many seconds for large (eg. 10 track-hours) projects
virtual void OnWaveformScaleType(wxCommandEvent &event);
virtual void OnSpectrumScaleType(wxCommandEvent &event);
virtual void OnZoomInVertical(wxCommandEvent &event);
virtual void OnZoomOutVertical(wxCommandEvent &event);
virtual void OnZoomFitVertical(wxCommandEvent &event);
// Find track info by coordinate
enum class CellType { Label, Track, VRuler, Background };
struct FoundCell {
@ -539,7 +512,6 @@ protected:
virtual void DrawEverythingElse(wxDC *dc, const wxRegion & region,
const wxRect & clip);
virtual void DrawOutside(Track *t, wxDC *dc, const wxRect & rec);
virtual void DrawZooming(wxDC* dc, const wxRect & clip);
virtual void HighlightFocusedTrack (wxDC* dc, const wxRect &rect);
virtual void DrawShadow (Track *t, wxDC* dc, const wxRect & rect);
@ -653,9 +625,6 @@ protected:
int mMouseMostRecentX;
int mMouseMostRecentY;
int mZoomStart;
int mZoomEnd;
// Handles snapping the selection boundaries or track boundaries to
// line up with existing tracks or labels. mSnapLeft and mSnapRight
// are the horizontal index of pixels to display user feedback
@ -725,7 +694,6 @@ public:
enum MouseCaptureEnum
{
IsUncaptured=0, // This is the normal state for the mouse
IsVZooming,
IsClosing,
IsSelecting,
IsAdjustingLabel,
@ -755,7 +723,6 @@ protected:
mArrowCursor, mSelectCursor,
mResizeCursor, mEnvelopeCursor, // doubles as the center frequency cursor
// for spectral selection
mZoomInCursor, mZoomOutCursor,
mRearrangeCursor,
mDisabledCursor, mAdjustLeftSelectionCursor, mAdjustRightSelectionCursor;
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
@ -767,9 +734,6 @@ protected:
mStretchCursor, mStretchLeftCursor, mStretchRightCursor;
#endif
std::unique_ptr<wxMenu>
mRulerWaveformMenu, mRulerSpectrumMenu;
Track *mPopupMenuTarget {};
friend class TrackPanelAx;

View File

@ -76,6 +76,7 @@ class NoteTrackMenuTable : public PopupMenuTable
public:
static NoteTrackMenuTable &Instance();
private:
void InitMenu(Menu*, void *pUserData) override
{
mpData = static_cast<TrackControls::InitMenuData*>(pUserData);

View File

@ -13,8 +13,193 @@ Paul Licameli split from TrackPanel.cpp
#ifdef USE_MIDI
#include "NoteTrackVRulerControls.h"
#include "../../../../HitTestResult.h"
#include "../../../../HitTestResult.h"
#include "../../../../NoteTrack.h"
#include "../../../../Project.h"
#include "../../../../RefreshCode.h"
#include "../../../../TrackPanelMouseEvent.h"
#include "../../../../UIHandle.h"
#include "../../../../../images/Cursors.h"
namespace
{
bool IsDragZooming(int zoomStart, int zoomEnd)
{
const int DragThreshold = 3;// Anything over 3 pixels is a drag, else a click.
return (abs(zoomEnd - zoomStart) > DragThreshold);
}
}
///////////////////////////////////////////////////////////////////////////////
class NoteTrackVZoomHandle : public UIHandle
{
NoteTrackVZoomHandle();
NoteTrackVZoomHandle(const NoteTrackVZoomHandle&);
NoteTrackVZoomHandle &operator=(const NoteTrackVZoomHandle&);
static NoteTrackVZoomHandle& Instance();
static HitTestPreview HitPreview(const wxMouseEvent &event);
public:
static HitTestResult HitTest(const wxMouseEvent &event);
virtual ~NoteTrackVZoomHandle();
virtual Result Click
(const TrackPanelMouseEvent &event, AudacityProject *pProject);
virtual Result Drag
(const TrackPanelMouseEvent &event, AudacityProject *pProject);
virtual HitTestPreview Preview
(const TrackPanelMouseEvent &event, const AudacityProject *pProject);
virtual Result Release
(const TrackPanelMouseEvent &event, AudacityProject *pProject,
wxWindow *pParent);
virtual Result Cancel(AudacityProject *pProject);
virtual void DrawExtras
(DrawingPass pass,
wxDC * dc, const wxRegion &updateRegion, const wxRect &panelRect);
void OnProjectChange(AudacityProject *pProject) override;
private:
NoteTrack *mpTrack;
int mZoomStart, mZoomEnd;
wxRect mRect;
};
NoteTrackVZoomHandle::NoteTrackVZoomHandle()
: mpTrack(NULL), mZoomStart(0), mZoomEnd(0), mRect()
{
}
NoteTrackVZoomHandle &NoteTrackVZoomHandle::Instance()
{
static NoteTrackVZoomHandle instance;
return instance;
}
HitTestPreview NoteTrackVZoomHandle::HitPreview(const wxMouseEvent &event)
{
static auto zoomInCursor =
::MakeCursor(wxCURSOR_MAGNIFIER, ZoomInCursorXpm, 19, 15);
static auto zoomOutCursor =
::MakeCursor(wxCURSOR_MAGNIFIER, ZoomOutCursorXpm, 19, 15);
return {
_("Click to verticaly zoom in, Shift-click to zoom out, Drag to create a particular zoom region."),
(event.ShiftDown() ? &*zoomOutCursor : &*zoomInCursor)
};
}
HitTestResult NoteTrackVZoomHandle::HitTest(const wxMouseEvent &event)
{
return HitTestResult(HitPreview(event), &Instance());
}
NoteTrackVZoomHandle::~NoteTrackVZoomHandle()
{
}
UIHandle::Result NoteTrackVZoomHandle::Click
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
mpTrack = static_cast<NoteTrack*>(
static_cast<NoteTrackVRulerControls*>(evt.pCell)->GetTrack()
);
mRect = evt.rect;
const wxMouseEvent &event = evt.event;
mZoomStart = event.m_y;
mZoomEnd = event.m_y;
// change note track to zoom like audio track
// mpTrack->StartVScroll();
return RefreshCode::RefreshNone;
}
UIHandle::Result NoteTrackVZoomHandle::Drag
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
const wxMouseEvent &event = evt.event;
mZoomEnd = event.m_y;
using namespace RefreshCode;
if (IsDragZooming(mZoomStart, mZoomEnd)) {
// changed Note track to work like audio track
// mpTrack->VScroll(mZoomStart, mZoomEnd);
return RefreshAll;
}
return RefreshNone;
}
HitTestPreview NoteTrackVZoomHandle::Preview
(const TrackPanelMouseEvent &evt, const AudacityProject *pProject)
{
return HitPreview(evt.event);
}
UIHandle::Result NoteTrackVZoomHandle::Release
(const TrackPanelMouseEvent &evt, AudacityProject *pProject,
wxWindow *pParent)
{
using namespace RefreshCode;
if (!mpTrack)
return RefreshNone;
const wxMouseEvent &event = evt.event;
if (IsDragZooming(mZoomStart, mZoomEnd)) {
mpTrack->ZoomTo(evt.rect, mZoomStart, mZoomEnd);
}
else if (event.ShiftDown() || event.RightUp()) {
mpTrack->ZoomOut(evt.rect, mZoomEnd);
}
else {
mpTrack->ZoomIn(evt.rect, mZoomEnd);
}
// TODO: shift-right click as in audio track?
mZoomEnd = mZoomStart = 0;
pProject->ModifyState(true);
return RefreshAll;
}
UIHandle::Result NoteTrackVZoomHandle::Cancel(AudacityProject *pProject)
{
// Cancel is implemented! And there is no initial state to restore,
// so just return a code.
return RefreshCode::RefreshAll;
}
void NoteTrackVZoomHandle::DrawExtras
(DrawingPass pass, wxDC * dc, const wxRegion &, const wxRect &panelRect)
{
if ( pass == UIHandle::Cells &&
IsDragZooming( mZoomStart, mZoomEnd ) )
TrackVRulerControls::DrawZooming
( dc, mRect, panelRect, mZoomStart, mZoomEnd );
}
void NoteTrackVZoomHandle::OnProjectChange(AudacityProject *pProject)
{
if (! pProject->GetTracks()->Contains(mpTrack)) {
mpTrack = nullptr;
mRect = {};
}
UIHandle::OnProjectChange(pProject);
}
///////////////////////////////////////////////////////////////////////////////
NoteTrackVRulerControls::NoteTrackVRulerControls()
: TrackVRulerControls()
{
@ -31,10 +216,14 @@ NoteTrackVRulerControls::~NoteTrackVRulerControls()
}
HitTestResult NoteTrackVRulerControls::HitTest
(const TrackPanelMouseEvent &,
(const TrackPanelMouseEvent &evt,
const AudacityProject *)
{
#ifdef USE_MIDI
return NoteTrackVZoomHandle::HitTest(evt.event);
#else
return {};
#endif
}
#endif

View File

@ -10,8 +10,652 @@ Paul Licameli split from TrackPanel.cpp
#include "../../../../Audacity.h"
#include "WaveTrackVRulerControls.h"
#include "../../../../HitTestResult.h"
#include <algorithm>
#include "../../../../HitTestResult.h"
#include "../../../../NumberScale.h"
#include "../../../../prefs/SpectrogramSettings.h"
#include "../../../../prefs/WaveformSettings.h"
#include "../../../../Project.h"
#include "../../../../RefreshCode.h"
#include "../../../../TrackPanelMouseEvent.h"
#include "../../../../UIHandle.h"
#include "../../../../WaveTrack.h"
#include "../../../../widgets/PopupMenuTable.h"
#include "../../../../../images/Cursors.h"
#include <wx/gdicmn.h>
#include <wx/dc.h>
namespace
{
struct InitMenuData
{
public:
WaveTrack *pTrack;
wxRect rect;
unsigned result;
int yy;
};
bool IsDragZooming(int zoomStart, int zoomEnd)
{
const int DragThreshold = 3;// Anything over 3 pixels is a drag, else a click.
return (abs(zoomEnd - zoomStart) > DragThreshold);
}
void HandleWaveTrackVZoom
(AudacityProject *pProject,
WaveTrack *pTrack, bool shiftDown, bool rightUp,
const wxRect &rect, int zoomStart, int zoomEnd,
bool fixedMousePoint)
{
static const float ZOOMLIMIT = 0.001f;
TrackList *const tracks = pProject->GetTracks();
// Assume linked track is wave or null
const auto partner = static_cast<WaveTrack *>(pTrack->GetLink());
int height = rect.height;
int ypos = rect.y;
// Ensure start and end are in order (swap if not).
if (zoomEnd < zoomStart)
std::swap( zoomStart, zoomEnd );
float min, max, c, minBand = 0;
const double rate = pTrack->GetRate();
const float halfrate = rate / 2;
const SpectrogramSettings &settings = pTrack->GetSpectrogramSettings();
NumberScale scale;
const bool spectral = (pTrack->GetDisplay() == WaveTrack::Spectrum);
const bool spectrumLinear = spectral &&
(pTrack->GetSpectrogramSettings().scaleType == SpectrogramSettings::stLinear);
if (spectral) {
pTrack->GetSpectrumBounds(&min, &max);
scale = (settings.GetScale(min, max));
const auto fftLength = settings.GetFFTLength();
const float binSize = rate / fftLength;
// JKC: Following discussions of Bug 1208 I'm allowing zooming in
// down to one bin.
// const int minBins =
// std::min(10, fftLength / 2); //minimum 10 freq bins, unless there are less
const int minBins = 1;
minBand = minBins * binSize;
}
else
pTrack->GetDisplayBounds(&min, &max);
if (IsDragZooming(zoomStart, zoomEnd)) {
// Drag Zoom
const float tmin = min, tmax = max;
if (spectral) {
double xmin = 1 - (zoomEnd - ypos) / (float)height;
double xmax = 1 - (zoomStart - ypos) / (float)height;
const float middle = (xmin + xmax) / 2;
const float middleValue = scale.PositionToValue(middle);
min = std::max(spectrumLinear ? 0.0f : 1.0f,
std::min(middleValue - minBand / 2,
scale.PositionToValue(xmin)
));
max = std::min(halfrate,
std::max(middleValue + minBand / 2,
scale.PositionToValue(xmax)
));
}
else {
const float p1 = (zoomStart - ypos) / (float)height;
const float p2 = (zoomEnd - ypos) / (float)height;
max = (tmax * (1.0 - p1) + tmin * p1);
min = (tmax * (1.0 - p2) + tmin * p2);
// Waveform view - allow zooming down to a range of ZOOMLIMIT
if (max - min < ZOOMLIMIT) { // if user attempts to go smaller...
c = (min + max) / 2; // ...set centre of view to centre of dragged area and top/bottom to ZOOMLIMIT/2 above/below
min = c - ZOOMLIMIT / 2.0;
max = c + ZOOMLIMIT / 2.0;
}
}
}
else if (shiftDown || rightUp) {
// Zoom OUT
if (spectral) {
if (shiftDown && rightUp) {
// Zoom out full
min = spectrumLinear ? 0.0f : 1.0f;
max = halfrate;
}
else {
// Zoom out
const float p1 = (zoomStart - ypos) / (float)height;
// (Used to zoom out centered at midline, ignoring the click, if linear view.
// I think it is better to be consistent. PRL)
// Center zoom-out at the midline
const float middle = // spectrumLinear ? 0.5f :
1.0f - p1;
if (fixedMousePoint) {
min = std::max(spectrumLinear ? 0.0f : 1.0f, scale.PositionToValue(-middle));
max = std::min(halfrate, scale.PositionToValue(1.0f + p1));
}
else {
min = std::max(spectrumLinear ? 0.0f : 1.0f, scale.PositionToValue(middle - 1.0f));
max = std::min(halfrate, scale.PositionToValue(middle + 1.0f));
}
}
}
else {
// Zoom out to -1.0...1.0 first, then, and only
// then, if they click again, allow one more
// zoom out.
if (shiftDown && rightUp) {
// Zoom out full
min = -1.0;
max = 1.0;
}
else {
// Zoom out
const WaveformSettings &settings = pTrack->GetWaveformSettings();
const bool linear = settings.isLinear();
const float top = linear
? 2.0
: (LINEAR_TO_DB(2.0) + settings.dBRange) / settings.dBRange;
if (min <= -1.0 && max >= 1.0) {
min = top;
max = top;
}
else {
// limit to +/- 1 range unless already outside that range...
float minRange = (min < -1) ? -top : -1.0;
float maxRange = (max > 1) ? top : 1.0;
// and enforce vertical zoom limits.
const float p1 = (zoomStart - ypos) / (float)height;
if (fixedMousePoint) {
const float oldRange = max - min;
const float c = (max * (1.0 - p1) + min * p1);
min = std::min(maxRange - ZOOMLIMIT,
std::max(minRange, c - 2 * (1.0f - p1) * oldRange));
max = std::max(minRange + ZOOMLIMIT,
std::min(maxRange, c + 2 * p1 * oldRange));
}
else {
const float c = p1 * min + (1 - p1) * max;
const float l = (max - min);
min = std::min(maxRange - ZOOMLIMIT,
std::max(minRange, c - l));
max = std::max(minRange + ZOOMLIMIT,
std::min(maxRange, c + l));
}
}
}
}
}
else {
// Zoom IN
if (spectral) {
// Center the zoom-in at the click
const float p1 = (zoomStart - ypos) / (float)height;
const float middle = 1.0f - p1;
const float middleValue = scale.PositionToValue(middle);
if (fixedMousePoint) {
min = std::max(spectrumLinear ? 0.0f : 1.0f,
std::min(middleValue - minBand * middle,
scale.PositionToValue(0.5f * middle)
));
max = std::min(halfrate,
std::max(middleValue + minBand * p1,
scale.PositionToValue(middle + 0.5f * p1)
));
}
else {
min = std::max(spectrumLinear ? 0.0f : 1.0f,
std::min(middleValue - minBand / 2,
scale.PositionToValue(middle - 0.25f)
));
max = std::min(halfrate,
std::max(middleValue + minBand / 2,
scale.PositionToValue(middle + 0.25f)
));
}
}
else {
// Zoom in centered on cursor
if (min < -1.0 || max > 1.0) {
min = -1.0;
max = 1.0;
}
else {
// Enforce maximum vertical zoom
const float oldRange = max - min;
const float l = std::max(ZOOMLIMIT, 0.5f * oldRange);
const float ratio = l / (max - min);
const float p1 = (zoomStart - ypos) / (float)height;
const float c = (max * (1.0 - p1) + min * p1);
if (fixedMousePoint)
min = c - ratio * (1.0f - p1) * oldRange,
max = c + ratio * p1 * oldRange;
else
min = c - 0.5 * l,
max = c + 0.5 * l;
}
}
}
if (spectral) {
pTrack->SetSpectrumBounds(min, max);
if (partner)
partner->SetSpectrumBounds(min, max);
}
else {
pTrack->SetDisplayBounds(min, max);
if (partner)
partner->SetDisplayBounds(min, max);
}
zoomEnd = zoomStart = 0;
pProject->ModifyState(true);
}
}
enum {
OnZoomInVerticalID = 20000,
OnZoomOutVerticalID,
OnZoomFitVerticalID,
// Reserve an ample block of ids for waveform scale types
OnFirstWaveformScaleID,
OnLastWaveformScaleID = OnFirstWaveformScaleID + 9,
// Reserve an ample block of ids for spectrum scale types
OnFirstSpectrumScaleID,
OnLastSpectrumScaleID = OnFirstSpectrumScaleID + 19,
};
///////////////////////////////////////////////////////////////////////////////
// Table class
class WaveTrackVRulerMenuTable : public PopupMenuTable
{
protected:
WaveTrackVRulerMenuTable() {}
void InitMenu(Menu *pMenu, void *pUserData) override;
private:
void DestroyMenu() override
{
mpData = nullptr;
}
protected:
InitMenuData *mpData {};
void OnZoomInVertical(wxCommandEvent&);
void OnZoomOutVertical(wxCommandEvent&);
void OnZoomFitVertical(wxCommandEvent&);
};
void WaveTrackVRulerMenuTable::InitMenu(Menu *, void *pUserData)
{
mpData = static_cast<InitMenuData*>(pUserData);
}
void WaveTrackVRulerMenuTable::OnZoomInVertical(wxCommandEvent &)
{
HandleWaveTrackVZoom
(::GetActiveProject(), mpData->pTrack, false, false, mpData->rect, mpData->yy, mpData->yy, false);
using namespace RefreshCode;
mpData->result = UpdateVRuler | RefreshAll;
}
void WaveTrackVRulerMenuTable::OnZoomOutVertical(wxCommandEvent &)
{
HandleWaveTrackVZoom
(::GetActiveProject(), mpData->pTrack, true, false, mpData->rect, mpData->yy, mpData->yy, false);
using namespace RefreshCode;
mpData->result = UpdateVRuler | RefreshAll;
}
void WaveTrackVRulerMenuTable::OnZoomFitVertical(wxCommandEvent &)
{
HandleWaveTrackVZoom
(::GetActiveProject(), mpData->pTrack, true, true, mpData->rect, mpData->yy, mpData->yy, false);
using namespace RefreshCode;
mpData->result = UpdateVRuler | RefreshAll;
}
///////////////////////////////////////////////////////////////////////////////
// Table class
class WaveformVRulerMenuTable : public WaveTrackVRulerMenuTable
{
WaveformVRulerMenuTable() : WaveTrackVRulerMenuTable() {}
virtual ~WaveformVRulerMenuTable() {}
DECLARE_POPUP_MENU(WaveformVRulerMenuTable);
public:
static WaveformVRulerMenuTable &Instance();
private:
virtual void InitMenu(Menu *pMenu, void *pUserData);
void OnWaveformScaleType(wxCommandEvent &evt);
};
WaveformVRulerMenuTable &WaveformVRulerMenuTable::Instance()
{
static WaveformVRulerMenuTable instance;
return instance;
}
void WaveformVRulerMenuTable::InitMenu(Menu *pMenu, void *pUserData)
{
WaveTrackVRulerMenuTable::InitMenu(pMenu, pUserData);
WaveTrack *const wt = mpData->pTrack;
const int id =
OnFirstWaveformScaleID + (int)(wt->GetWaveformSettings().scaleType);
pMenu->Check(id, true);
}
BEGIN_POPUP_MENU(WaveformVRulerMenuTable)
{
const wxArrayString & names = WaveformSettings::GetScaleNames();
for (int ii = 0, nn = names.size(); ii < nn; ++ii) {
POPUP_MENU_RADIO_ITEM(OnFirstWaveformScaleID + ii, names[ii],
OnWaveformScaleType);
}
}
POPUP_MENU_SEPARATOR()
POPUP_MENU_ITEM(OnZoomInVerticalID, _("Zoom In\tLeft-Click/Left-Drag"), OnZoomInVertical)
POPUP_MENU_ITEM(OnZoomOutVerticalID, _("Zoom Out\tShift-Left-Click"), OnZoomOutVertical)
POPUP_MENU_ITEM(OnZoomFitVerticalID, _("Zoom to Fit\tShift-Right-Click"), OnZoomFitVertical)
END_POPUP_MENU()
void WaveformVRulerMenuTable::OnWaveformScaleType(wxCommandEvent &evt)
{
WaveTrack *const wt = mpData->pTrack;
// Assume linked track is wave or null
const auto partner = static_cast<WaveTrack*>(wt->GetLink());
const WaveformSettings::ScaleType newScaleType =
WaveformSettings::ScaleType(
std::max(0,
std::min((int)(WaveformSettings::stNumScaleTypes) - 1,
evt.GetId() - OnFirstWaveformScaleID
)));
if (wt->GetWaveformSettings().scaleType != newScaleType) {
wt->GetIndependentWaveformSettings().scaleType = newScaleType;
if (partner)
partner->GetIndependentWaveformSettings().scaleType = newScaleType;
::GetActiveProject()->ModifyState(true);
using namespace RefreshCode;
mpData->result = UpdateVRuler | RefreshAll;
}
}
///////////////////////////////////////////////////////////////////////////////
// Table class
class SpectrumVRulerMenuTable : public WaveTrackVRulerMenuTable
{
SpectrumVRulerMenuTable() : WaveTrackVRulerMenuTable() {}
virtual ~SpectrumVRulerMenuTable() {}
DECLARE_POPUP_MENU(SpectrumVRulerMenuTable);
public:
static SpectrumVRulerMenuTable &Instance();
private:
void InitMenu(Menu *pMenu, void *pUserData);
void OnSpectrumScaleType(wxCommandEvent &evt);
};
SpectrumVRulerMenuTable &SpectrumVRulerMenuTable::Instance()
{
static SpectrumVRulerMenuTable instance;
return instance;
}
void SpectrumVRulerMenuTable::InitMenu(Menu *pMenu, void *pUserData)
{
WaveTrackVRulerMenuTable::InitMenu(pMenu, pUserData);
WaveTrack *const wt = mpData->pTrack;
const int id =
OnFirstSpectrumScaleID + (int)(wt->GetSpectrogramSettings().scaleType);
pMenu->Check(id, true);
}
BEGIN_POPUP_MENU(SpectrumVRulerMenuTable)
{
const wxArrayString & names = SpectrogramSettings::GetScaleNames();
for (int ii = 0, nn = names.size(); ii < nn; ++ii) {
POPUP_MENU_RADIO_ITEM(OnFirstSpectrumScaleID + ii, names[ii],
OnSpectrumScaleType);
}
}
POPUP_MENU_SEPARATOR()
POPUP_MENU_ITEM(OnZoomInVerticalID, _("Zoom In\tLeft-Click/Left-Drag"), OnZoomInVertical)
POPUP_MENU_ITEM(OnZoomOutVerticalID, _("Zoom Out\tShift-Left-Click"), OnZoomOutVertical)
POPUP_MENU_ITEM(OnZoomFitVerticalID, _("Zoom to Fit\tShift-Right-Click"), OnZoomFitVertical)
END_POPUP_MENU()
void SpectrumVRulerMenuTable::OnSpectrumScaleType(wxCommandEvent &evt)
{
WaveTrack *const wt = mpData->pTrack;
// Assume linked track is wave or null
const auto partner = static_cast<WaveTrack*>(wt->GetLink());
const SpectrogramSettings::ScaleType newScaleType =
SpectrogramSettings::ScaleType(
std::max(0,
std::min((int)(SpectrogramSettings::stNumScaleTypes) - 1,
evt.GetId() - OnFirstSpectrumScaleID
)));
if (wt->GetSpectrogramSettings().scaleType != newScaleType) {
wt->GetIndependentSpectrogramSettings().scaleType = newScaleType;
if (partner)
partner->GetIndependentSpectrogramSettings().scaleType = newScaleType;
::GetActiveProject()->ModifyState(true);
using namespace RefreshCode;
mpData->result = UpdateVRuler | RefreshAll;
}
}
///////////////////////////////////////////////////////////////////////////////
class WaveTrackVZoomHandle : public UIHandle
{
WaveTrackVZoomHandle();
WaveTrackVZoomHandle(const WaveTrackVZoomHandle&);
WaveTrackVZoomHandle &operator=(const WaveTrackVZoomHandle&);
static WaveTrackVZoomHandle& Instance();
static HitTestPreview HitPreview(const wxMouseEvent &event);
public:
static HitTestResult HitTest(const wxMouseEvent &event);
virtual ~WaveTrackVZoomHandle();
virtual Result Click
(const TrackPanelMouseEvent &event, AudacityProject *pProject);
virtual Result Drag
(const TrackPanelMouseEvent &event, AudacityProject *pProject);
virtual HitTestPreview Preview
(const TrackPanelMouseEvent &event, const AudacityProject *pProject);
virtual Result Release
(const TrackPanelMouseEvent &event, AudacityProject *pProject,
wxWindow *pParent);
virtual Result Cancel(AudacityProject *pProject);
virtual void DrawExtras
(DrawingPass pass,
wxDC * dc, const wxRegion &updateRegion, const wxRect &panelRect);
void OnProjectChange(AudacityProject *pProject) override;
private:
WaveTrack *mpTrack{};
int mZoomStart{}, mZoomEnd{};
wxRect mRect{};
};
WaveTrackVZoomHandle::WaveTrackVZoomHandle()
{
}
WaveTrackVZoomHandle &WaveTrackVZoomHandle::Instance()
{
static WaveTrackVZoomHandle instance;
return instance;
}
HitTestPreview WaveTrackVZoomHandle::HitPreview(const wxMouseEvent &event)
{
static auto zoomInCursor =
::MakeCursor(wxCURSOR_MAGNIFIER, ZoomInCursorXpm, 19, 15);
static auto zoomOutCursor =
::MakeCursor(wxCURSOR_MAGNIFIER, ZoomOutCursorXpm, 19, 15);
return {
_("Click to vertically zoom in. Shift-click to zoom out. Drag to specify a zoom region."),
(event.ShiftDown() ? &*zoomOutCursor : &*zoomInCursor)
};
}
HitTestResult WaveTrackVZoomHandle::HitTest(const wxMouseEvent &event)
{
return { HitPreview(event), &Instance() };
}
WaveTrackVZoomHandle::~WaveTrackVZoomHandle()
{
}
UIHandle::Result WaveTrackVZoomHandle::Click
(const TrackPanelMouseEvent &evt, AudacityProject *)
{
mpTrack = static_cast<WaveTrack*>(
static_cast<WaveTrackVRulerControls*>(evt.pCell)->GetTrack()
);
mRect = evt.rect;
const wxMouseEvent &event = evt.event;
mZoomStart = event.m_y;
mZoomEnd = event.m_y;
return RefreshCode::RefreshNone;
}
UIHandle::Result WaveTrackVZoomHandle::Drag
(const TrackPanelMouseEvent &evt, AudacityProject *)
{
const wxMouseEvent &event = evt.event;
mZoomEnd = event.m_y;
using namespace RefreshCode;
if (IsDragZooming(mZoomStart, mZoomEnd))
return RefreshAll;
return RefreshNone;
}
HitTestPreview WaveTrackVZoomHandle::Preview
(const TrackPanelMouseEvent &evt, const AudacityProject *)
{
return HitPreview(evt.event);
}
UIHandle::Result WaveTrackVZoomHandle::Release
(const TrackPanelMouseEvent &evt, AudacityProject *pProject,
wxWindow *pParent)
{
using namespace RefreshCode;
if (!mpTrack)
return RefreshNone;
const wxMouseEvent &event = evt.event;
const bool shiftDown = event.ShiftDown();
const bool rightUp = event.RightUp();
// Popup menu... disabled
if (false &&
rightUp &&
!(event.ShiftDown() || event.CmdDown()))
{
InitMenuData data {
mpTrack, mRect, RefreshCode::RefreshNone, event.m_y
};
PopupMenuTable *const pTable =
(mpTrack->GetDisplay() == WaveTrack::Spectrum)
? (PopupMenuTable *) &SpectrumVRulerMenuTable::Instance()
: (PopupMenuTable *) &WaveformVRulerMenuTable::Instance();
std::unique_ptr<PopupMenuTable::Menu>
pMenu(PopupMenuTable::BuildMenu(pParent, pTable, &data));
pParent->PopupMenu(pMenu.get(), event.m_x, event.m_y);
return data.result;
}
else
HandleWaveTrackVZoom(pProject, mpTrack, shiftDown, rightUp,
mRect, mZoomStart, mZoomEnd, false);
return UpdateVRuler | RefreshAll;
}
UIHandle::Result WaveTrackVZoomHandle::Cancel(AudacityProject*)
{
// Cancel is implemented! And there is no initial state to restore,
// so just return a code.
return RefreshCode::RefreshAll;
}
void WaveTrackVZoomHandle::DrawExtras
(DrawingPass pass, wxDC * dc, const wxRegion &, const wxRect &panelRect)
{
if ( pass == UIHandle::Cells &&
IsDragZooming( mZoomStart, mZoomEnd ) )
TrackVRulerControls::DrawZooming
( dc, mRect, panelRect, mZoomStart, mZoomEnd );
}
void WaveTrackVZoomHandle::OnProjectChange(AudacityProject *pProject)
{
if (! pProject->GetTracks()->Contains(mpTrack)) {
mpTrack = nullptr;
mRect = {};
}
UIHandle::OnProjectChange(pProject);
}
///////////////////////////////////////////////////////////////////////////////
WaveTrackVRulerControls::WaveTrackVRulerControls()
: TrackVRulerControls()
{
@ -28,8 +672,140 @@ WaveTrackVRulerControls::~WaveTrackVRulerControls()
}
HitTestResult WaveTrackVRulerControls::HitTest
(const TrackPanelMouseEvent &,
(const TrackPanelMouseEvent &evt,
const AudacityProject *)
{
return {};
return WaveTrackVZoomHandle::HitTest(evt.event);
}
unsigned WaveTrackVRulerControls::HandleWheelRotation
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
using namespace RefreshCode;
const wxMouseEvent &event = evt.event;
if (!(event.ShiftDown() || event.CmdDown()))
return RefreshNone;
// Always stop propagation even if the ruler didn't change. The ruler
// is a narrow enough target.
evt.event.Skip(false);
Track *const pTrack = GetTrack();
wxASSERT(pTrack->GetKind() == Track::Wave);
auto steps = evt.steps;
WaveTrack *const wt = static_cast<WaveTrack*>(pTrack);
// Assume linked track is wave or null
const auto partner = static_cast<WaveTrack*>(wt->GetLink());
const bool isDB =
wt->GetDisplay() == WaveTrack::Waveform &&
wt->GetWaveformSettings().scaleType == WaveformSettings::stLogarithmic;
// Special cases for Waveform dB only.
// Set the bottom of the dB scale but only if it's visible
if (isDB && event.ShiftDown() && event.CmdDown()) {
float min, max;
wt->GetDisplayBounds(&min, &max);
if (!(min < 0.0 && max > 0.0))
return RefreshNone;
WaveformSettings &settings = wt->GetIndependentWaveformSettings();
float olddBRange = settings.dBRange;
if (steps < 0)
// Zoom out
settings.NextLowerDBRange();
else
settings.NextHigherDBRange();
float newdBRange = settings.dBRange;
if (partner) {
WaveformSettings &settings = partner->GetIndependentWaveformSettings();
if (steps < 0)
// Zoom out
settings.NextLowerDBRange();
else
settings.NextHigherDBRange();
}
// Is y coordinate within the rectangle half-height centered about
// the zero level?
const auto &rect = evt.rect;
const auto zeroLevel = wt->ZeroLevelYCoordinate(rect);
const bool fixedMagnification =
(4 * std::abs(event.GetY() - zeroLevel) < rect.GetHeight());
if (fixedMagnification) {
// Vary the db limit without changing
// magnification; that is, peaks and troughs move up and down
// rigidly, as parts of the wave near zero are exposed or hidden.
const float extreme = (LINEAR_TO_DB(2) + newdBRange) / newdBRange;
max = std::min(extreme, max * olddBRange / newdBRange);
min = std::max(-extreme, min * olddBRange / newdBRange);
wt->SetLastdBRange();
wt->SetDisplayBounds(min, max);
if (partner) {
partner->SetLastdBRange();
partner->SetDisplayBounds(min, max);
}
}
}
else if (event.CmdDown() && !event.ShiftDown()) {
const int yy = event.m_y;
HandleWaveTrackVZoom(
pProject, wt, false, (steps < 0),
evt.rect, yy, yy, true);
}
else if (!event.CmdDown() && event.ShiftDown()) {
// Scroll some fixed number of pixels, independent of zoom level or track height:
static const float movement = 10.0f;
const int height = evt.rect.GetHeight();
const bool spectral = (wt->GetDisplay() == WaveTrack::Spectrum);
if (spectral) {
const float delta = steps * movement / height;
SpectrogramSettings &settings = wt->GetIndependentSpectrogramSettings();
const bool isLinear = settings.scaleType == SpectrogramSettings::stLinear;
float bottom, top;
wt->GetSpectrumBounds(&bottom, &top);
const double rate = wt->GetRate();
const float bound = rate / 2;
const NumberScale numberScale(settings.GetScale(bottom, top));
float newTop =
std::min(bound, numberScale.PositionToValue(1.0f + delta));
const float newBottom =
std::max((isLinear ? 0.0f : 1.0f),
numberScale.PositionToValue(numberScale.ValueToPosition(newTop) - 1.0f));
newTop =
std::min(bound,
numberScale.PositionToValue(numberScale.ValueToPosition(newBottom) + 1.0f));
wt->SetSpectrumBounds(newBottom, newTop);
if (partner)
partner->SetSpectrumBounds(newBottom, newTop);
}
else {
float topLimit = 2.0;
if (isDB) {
const float dBRange = wt->GetWaveformSettings().dBRange;
topLimit = (LINEAR_TO_DB(topLimit) + dBRange) / dBRange;
}
const float bottomLimit = -topLimit;
float top, bottom;
wt->GetDisplayBounds(&bottom, &top);
const float range = top - bottom;
const float delta = range * steps * movement / height;
float newTop = std::min(topLimit, top + delta);
const float newBottom = std::max(bottomLimit, newTop - range);
newTop = std::min(topLimit, newBottom + range);
wt->SetDisplayBounds(newBottom, newTop);
if (partner)
partner->SetDisplayBounds(newBottom, newTop);
}
}
else
return RefreshNone;
pProject->ModifyState(true);
return RefreshCell | UpdateVRuler;
}

View File

@ -26,6 +26,10 @@ public:
HitTestResult HitTest
(const TrackPanelMouseEvent &event,
const AudacityProject *) override;
unsigned HandleWheelRotation
(const TrackPanelMouseEvent &event,
AudacityProject *pProject) override;
};
#endif

View File

@ -12,6 +12,8 @@ Paul Licameli split from TrackPanel.cpp
#include "../../HitTestResult.h"
#include "TrackVRulerControls.h"
#include "../../TrackPanel.h"
#include <wx/cursor.h>
#include <wx/translation.h>
@ -30,4 +32,26 @@ HitTestResult TrackVRulerControls::HitTest
// Use a space for the tip, otherwise we get the default message.
static wxCursor arrowCursor{ wxCURSOR_ARROW };
return { { _(" "), &arrowCursor }, nullptr };
}
void TrackVRulerControls::DrawZooming
( wxDC *dc, const wxRect &cellRect, const wxRect &panelRect,
int zoomStart, int zoomEnd )
{
// Draw a dashed rectangle, its right side disappearing in the black right
// border of the track area, which is not part of this cell but right of it.
wxRect rect;
dc->SetBrush(*wxTRANSPARENT_BRUSH);
dc->SetPen(*wxBLACK_DASHED_PEN);
rect.y = std::min( zoomStart, zoomEnd);
rect.height = 1 + abs( zoomEnd - zoomStart);
rect.x = cellRect.x;
// TODO: Don't use the constant kRightMargin, but somehow discover the
// neighboring track rectangle
rect.SetRight(panelRect.GetWidth() - kRightMargin);
dc->DrawRectangle(rect);
}

View File

@ -14,6 +14,7 @@ Paul Licameli split from TrackPanel.cpp
#include "CommonTrackPanelCell.h"
class Track;
class wxDC;
class TrackVRulerControls /* not final */ : public CommonTrackPanelCell
{
@ -29,6 +30,10 @@ public:
(const TrackPanelMouseEvent &event,
const AudacityProject *pProject) override;
static void DrawZooming
( wxDC *dc, const wxRect &cellRect, const wxRect &panelRect,
int zoomStart, int zoomEnd);
protected:
Track *FindTrack() override;