1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-12-27 23:18:46 +01:00

VZooming Changes

1: Guard zone 8 pixels wide in VRuler to reduce risk of accidental use.
2: VerticalZooming preference, for VRuler zooming, off by default,
3: Enabled Paul's right-click menu in VRuler with some extra
presets (x2 and x0.5)
4: Rearranged some messy code with lots of ifs to use a switch.

Also, with EXPERIMENTAL_HALF_WAVE defined we now get a
'half wave' option in the VRuler menu, that shows the wave top half.
We also use it in collapsed waves (and collapsing and restoring
a WaveTrack will get you back to normal zoom.
This commit is contained in:
James Crook
2017-12-11 18:41:48 +00:00
parent 8a1f5bb748
commit 13c6fdf663
11 changed files with 355 additions and 145 deletions

View File

@@ -45,6 +45,22 @@ std::vector<UIHandlePtr> WaveTrackVRulerControls::HitTest
return results;
}
void WaveTrackVRulerControls::DoZoomPreset( int i)
{
const auto pTrack = FindTrack();
if (!pTrack)
return;
wxASSERT(pTrack->GetKind() == Track::Wave);
const auto wt = static_cast<WaveTrack*>(pTrack.get());
WaveTrackVZoomHandle::DoZoom(
NULL, wt, (i==1)?kZoomHalfWave: kZoom1to1,
wxRect(0,0,0,0), 0,0, true);
}
unsigned WaveTrackVRulerControls::HandleWheelRotation
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
@@ -122,7 +138,7 @@ unsigned WaveTrackVRulerControls::HandleWheelRotation
else if (event.CmdDown() && !event.ShiftDown()) {
const int yy = event.m_y;
WaveTrackVZoomHandle::DoZoom(
pProject, wt, false, (steps < 0),
pProject, wt, (steps < 0)?kZoomOut:kZoomIn,
evt.rect, yy, yy, true);
}
else if (!event.CmdDown() && event.ShiftDown()) {

View File

@@ -33,7 +33,7 @@ public:
unsigned HandleWheelRotation
(const TrackPanelMouseEvent &event,
AudacityProject *pProject) override;
void DoZoomPreset( int i);
private:
std::weak_ptr<WaveTrackVZoomHandle> mVZoomHandle;
};

View File

@@ -23,6 +23,7 @@ Paul Licameli split from TrackPanel.cpp
#include "../../../../WaveTrack.h"
#include "../../../../widgets/PopupMenuTable.h"
#include "../../../../../images/Cursors.h"
#include "../../../../Prefs.h"
namespace
{
@@ -39,7 +40,9 @@ public:
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);
bool bVZoom;
gPrefs->Read(wxT("/GUI/VerticalZooming"), &bVZoom, false);
return bVZoom && (abs(zoomEnd - zoomStart) > DragThreshold);
}
}
@@ -58,9 +61,12 @@ void WaveTrackVZoomHandle::Enter(bool)
#endif
}
// ZoomKind says how to zoom.
// If ZoomStart and ZoomEnd are not equal, this may override
// the zoomKind and cause a drag-zoom-in.
void WaveTrackVZoomHandle::DoZoom
(AudacityProject *pProject,
WaveTrack *pTrack, bool shiftDown, bool rightUp,
WaveTrack *pTrack, int ZoomKind,
const wxRect &rect, int zoomStart, int zoomEnd,
bool fixedMousePoint)
{
@@ -83,6 +89,20 @@ void WaveTrackVZoomHandle::DoZoom
const bool spectrumLinear = spectral &&
(pTrack->GetSpectrogramSettings().scaleType == SpectrogramSettings::stLinear);
bool bDragZoom = IsDragZooming(zoomStart, zoomEnd);
const int kSpectral = 8;
// Possibly override the zoom kind.
if( bDragZoom )
ZoomKind = kZoomInByDrag;
// If we are actually zooming a spectrum rather than a wave.
ZoomKind += spectral ? kSpectral:0;
float top=2.0;
float half=0.5;
if (spectral) {
pTrack->GetSpectrumBounds(&min, &max);
scale = (settings.GetScale(min, max));
@@ -96,14 +116,141 @@ void WaveTrackVZoomHandle::DoZoom
const int minBins = 1;
minBand = minBins * binSize;
}
else
else{
pTrack->GetDisplayBounds(&min, &max);
const WaveformSettings &settings = pTrack->GetWaveformSettings();
const bool linear = settings.isLinear();
if( !linear ){
top = (LINEAR_TO_DB(2.0) + settings.dBRange) / settings.dBRange;
half = (LINEAR_TO_DB(0.5) + settings.dBRange) / settings.dBRange;
}
}
if (IsDragZooming(zoomStart, zoomEnd)) {
// Drag Zoom
const float tmin = min, tmax = max;
if (spectral) {
// Compute min and max.
switch(ZoomKind)
{
default:
// If we have covered all the cases, this won't happen.
// In release builds Audacity will ignore the zoom.
wxFAIL_MSG("Zooming Case not implemented by Audacity");
break;
case kZoom1to1:
{
// Zoom out full
min = -1.0;
max = 1.0;
}
break;
case kZoomDiv2:
{
// Zoom out even more than full :-)
// -2.0..+2.0 (or logarithmic equivalent)
min = -top;
max = top;
}
break;
case kZoomTimes2:
{
// Zoom in to -0.5..+0.5
min = -half;
max = half;
}
break;
case kZoomHalfWave:
{
// Zoom to show fractionally more than the top half of the wave.
min = -0.01f;
max = 1.0;
}
break;
case kZoomInByDrag:
{
const float tmin = min, tmax = max;
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;
}
}
break;
case kZoomIn:
{
// 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;
}
}
break;
case kZoomOut:
{
// Zoom out
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));
}
}
}
break;
// VZooming on spectral we don't implement the other zoom presets.
// They are also not in the menu.
case kZoom1to1 + kSpectral:
case kZoomDiv2 + kSpectral:
case kZoomTimes2 + kSpectral:
case kZoomHalfWave + kSpectral:
{
// Zoom out full
min = spectrumLinear ? 0.0f : 1.0f;
max = halfrate;
}
break;
case kZoomInByDrag + kSpectral:
{
double xmin = 1 - (zoomEnd - ypos) / (float)height;
double xmax = 1 - (zoomStart - ypos) / (float)height;
const float middle = (xmin + xmax) / 2;
@@ -118,97 +265,9 @@ void WaveTrackVZoomHandle::DoZoom
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) {
break;
case kZoomIn + kSpectral:
{
// Center the zoom-in at the click
const float p1 = (zoomStart - ypos) / (float)height;
const float middle = 1.0f - p1;
@@ -235,30 +294,30 @@ void WaveTrackVZoomHandle::DoZoom
));
}
}
else {
// Zoom in centered on cursor
if (min < -1.0 || max > 1.0) {
min = -1.0;
max = 1.0;
break;
case kZoomOut + kSpectral:
{
// 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 {
// 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;
min = std::max(spectrumLinear ? 0.0f : 1.0f, scale.PositionToValue(middle - 1.0f));
max = std::min(halfrate, scale.PositionToValue(middle + 1.0f));
}
}
break;
}
// Now actually apply the zoom.
if (spectral) {
pTrack->SetSpectrumBounds(min, max);
if (partner)
@@ -271,13 +330,17 @@ void WaveTrackVZoomHandle::DoZoom
}
zoomEnd = zoomStart = 0;
pProject->ModifyState(true);
if( pProject )
pProject->ModifyState(true);
}
enum {
OnZoomInVerticalID = 20000,
OnZoomFitVerticalID = 20000,
OnZoomDiv2ID,
OnZoomTimes2ID,
OnZoomHalfWaveID,
OnZoomInVerticalID,
OnZoomOutVerticalID,
OnZoomFitVerticalID,
// Reserve an ample block of ids for waveform scale types
OnFirstWaveformScaleID,
@@ -307,9 +370,12 @@ private:
protected:
InitMenuData *mpData {};
void OnZoomFitVertical(wxCommandEvent&);
void OnZoomDiv2Vertical(wxCommandEvent&);
void OnZoomTimes2Vertical(wxCommandEvent&);
void OnZoomHalfWave(wxCommandEvent&);
void OnZoomInVertical(wxCommandEvent&);
void OnZoomOutVertical(wxCommandEvent&);
void OnZoomFitVertical(wxCommandEvent&);
};
void WaveTrackVRulerMenuTable::InitMenu(Menu *, void *pUserData)
@@ -320,7 +386,7 @@ void WaveTrackVRulerMenuTable::InitMenu(Menu *, void *pUserData)
void WaveTrackVRulerMenuTable::OnZoomInVertical(wxCommandEvent &)
{
WaveTrackVZoomHandle::DoZoom
(::GetActiveProject(), mpData->pTrack, false, false, mpData->rect, mpData->yy, mpData->yy, false);
(::GetActiveProject(), mpData->pTrack, kZoomIn, mpData->rect, mpData->yy, mpData->yy, false);
using namespace RefreshCode;
mpData->result = UpdateVRuler | RefreshAll;
@@ -329,7 +395,7 @@ void WaveTrackVRulerMenuTable::OnZoomInVertical(wxCommandEvent &)
void WaveTrackVRulerMenuTable::OnZoomOutVertical(wxCommandEvent &)
{
WaveTrackVZoomHandle::DoZoom
(::GetActiveProject(), mpData->pTrack, true, false, mpData->rect, mpData->yy, mpData->yy, false);
(::GetActiveProject(), mpData->pTrack, kZoomOut, mpData->rect, mpData->yy, mpData->yy, false);
using namespace RefreshCode;
mpData->result = UpdateVRuler | RefreshAll;
@@ -338,7 +404,34 @@ void WaveTrackVRulerMenuTable::OnZoomOutVertical(wxCommandEvent &)
void WaveTrackVRulerMenuTable::OnZoomFitVertical(wxCommandEvent &)
{
WaveTrackVZoomHandle::DoZoom
(::GetActiveProject(), mpData->pTrack, true, true, mpData->rect, mpData->yy, mpData->yy, false);
(::GetActiveProject(), mpData->pTrack, kZoom1to1, mpData->rect, mpData->yy, mpData->yy, false);
using namespace RefreshCode;
mpData->result = UpdateVRuler | RefreshAll;
}
void WaveTrackVRulerMenuTable::OnZoomDiv2Vertical(wxCommandEvent &)
{
WaveTrackVZoomHandle::DoZoom
(::GetActiveProject(), mpData->pTrack, kZoomDiv2, mpData->rect, mpData->yy, mpData->yy, false);
using namespace RefreshCode;
mpData->result = UpdateVRuler | RefreshAll;
}
void WaveTrackVRulerMenuTable::OnZoomTimes2Vertical(wxCommandEvent &)
{
WaveTrackVZoomHandle::DoZoom
(::GetActiveProject(), mpData->pTrack, kZoomTimes2, mpData->rect, mpData->yy, mpData->yy, false);
using namespace RefreshCode;
mpData->result = UpdateVRuler | RefreshAll;
}
void WaveTrackVRulerMenuTable::OnZoomHalfWave(wxCommandEvent &)
{
WaveTrackVZoomHandle::DoZoom
(::GetActiveProject(), mpData->pTrack, kZoomHalfWave, mpData->rect, mpData->yy, mpData->yy, false);
using namespace RefreshCode;
mpData->result = UpdateVRuler | RefreshAll;
@@ -372,14 +465,32 @@ void WaveformVRulerMenuTable::InitMenu(Menu *pMenu, void *pUserData)
{
WaveTrackVRulerMenuTable::InitMenu(pMenu, pUserData);
// DB setting is already on track drop down.
#if 0
WaveTrack *const wt = mpData->pTrack;
const int id =
OnFirstWaveformScaleID + (int)(wt->GetWaveformSettings().scaleType);
pMenu->Check(id, true);
#endif
}
BEGIN_POPUP_MENU(WaveformVRulerMenuTable)
POPUP_MENU_ITEM(OnZoomFitVerticalID, _("Zoom Reset\tShift-Right-Click"), OnZoomFitVertical)
POPUP_MENU_ITEM(OnZoomDiv2ID, _("Zoom x1/2"), OnZoomDiv2Vertical)
POPUP_MENU_ITEM(OnZoomTimes2ID, _("Zoom x2"), OnZoomTimes2Vertical)
#ifdef EXPERIMENTAL_HALF_WAVE
POPUP_MENU_ITEM(OnZoomHalfWaveID, _("Half Wave"), OnZoomHalfWave)
#endif
POPUP_MENU_SEPARATOR()
POPUP_MENU_ITEM(OnZoomInVerticalID, _("Zoom In\tLeft-Click/Left-Drag"), OnZoomInVertical)
POPUP_MENU_ITEM(OnZoomOutVerticalID, _("Zoom Out\tShift-Left-Click"), OnZoomOutVertical)
// The log and linear options are already available as waveform db.
// So don't repeat them here.
#if 0
POPUP_MENU_SEPARATOR()
{
const wxArrayString & names = WaveformSettings::GetScaleNames();
for (int ii = 0, nn = names.size(); ii < nn; ++ii) {
@@ -387,11 +498,7 @@ BEGIN_POPUP_MENU(WaveformVRulerMenuTable)
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)
#endif
END_POPUP_MENU()
void WaveformVRulerMenuTable::OnWaveformScaleType(wxCommandEvent &evt)
@@ -498,11 +605,17 @@ HitTestPreview WaveTrackVZoomHandle::HitPreview(const wxMouseState &state)
::MakeCursor(wxCURSOR_MAGNIFIER, ZoomInCursorXpm, 19, 15);
static auto zoomOutCursor =
::MakeCursor(wxCURSOR_MAGNIFIER, ZoomOutCursorXpm, 19, 15);
const auto message =
_("Click to vertically zoom in. Shift-click to zoom out. Drag to specify a zoom region.");
static wxCursor arrowCursor{ wxCURSOR_ARROW };
bool bVZoom;
gPrefs->Read(wxT("/GUI/VerticalZooming"), &bVZoom, false);
bVZoom &= !state.RightIsDown();
const auto message = bVZoom ?
_("Click to vertically zoom in. Shift-click to zoom out. Drag to specify a zoom region.") :
_("Right-click for menu.");
return {
message,
(state.ShiftDown() ? &*zoomOutCursor : &*zoomInCursor)
bVZoom ? (state.ShiftDown() ? &*zoomOutCursor : &*zoomInCursor) : &arrowCursor
// , message
};
}
@@ -526,6 +639,8 @@ UIHandle::Result WaveTrackVZoomHandle::Drag
return Cancelled;
const wxMouseEvent &event = evt.event;
if ( event.RightIsDown() )
return RefreshNone;
mZoomEnd = event.m_y;
if (IsDragZooming(mZoomStart, mZoomEnd))
return RefreshAll;
@@ -551,8 +666,8 @@ UIHandle::Result WaveTrackVZoomHandle::Release
const bool shiftDown = event.ShiftDown();
const bool rightUp = event.RightUp();
// Popup menu... disabled
if (false &&
// Popup menu...
if (
rightUp &&
!(event.ShiftDown() || event.CmdDown()))
{
@@ -566,14 +681,44 @@ UIHandle::Result WaveTrackVZoomHandle::Release
: (PopupMenuTable *) &WaveformVRulerMenuTable::Instance();
std::unique_ptr<PopupMenuTable::Menu>
pMenu(PopupMenuTable::BuildMenu(pParent, pTable, &data));
bool bVZoom;
gPrefs->Read(wxT("/GUI/VerticalZooming"), &bVZoom, false);
// Accelerators only if zooming enabled.
if( !bVZoom )
{
wxMenuItemList & L = pMenu->GetMenuItems();
// let's iterate over the list in STL syntax
wxMenuItemList::iterator iter;
for (iter = L.begin(); iter != L.end(); ++iter)
{
wxMenuItem *pItem = *iter;
// Remove accelerator, if any.
pItem->SetText( (pItem->GetText() + "\t" ).BeforeFirst('\t') );
}
}
pParent->PopupMenu(pMenu.get(), event.m_x, event.m_y);
return data.result;
}
else
DoZoom(pProject, pTrack.get(), shiftDown, rightUp,
mRect, mZoomStart, mZoomEnd, false);
else{
bool bVZoom;
gPrefs->Read(wxT("/GUI/VerticalZooming"), &bVZoom, false);
// Ignore Capture Lost event
bVZoom &= event.GetId() != kCaptureLostEventId;
// shiftDown | rightUp | ZoomKind
// T | T | 1to1
// T | F | Out
// F | - | In
if( bVZoom ){
if( shiftDown )
mZoomStart=mZoomEnd;
DoZoom(pProject, pTrack.get(), shiftDown ? (rightUp ? kZoom1to1 : kZoomOut) : kZoomIn,
mRect, mZoomStart, mZoomEnd, false);
}
}
return UpdateVRuler | RefreshAll;
}

View File

@@ -17,6 +17,17 @@ class WaveTrack;
#include "../../../../MemoryX.h"
#include "../../../../UIHandle.h"
// Note that these can be with or without spectrum view which
// adds a constant.
const int kZoom1to1 = 1;
const int kZoomTimes2 = 2;
const int kZoomDiv2 = 3;
const int kZoomHalfWave = 4;
const int kZoomInByDrag = 5;
const int kZoomIn = 6;
const int kZoomOut = 7;
class WaveTrackVZoomHandle : public UIHandle
{
WaveTrackVZoomHandle(const WaveTrackVZoomHandle&);
@@ -30,7 +41,7 @@ public:
static void DoZoom
(AudacityProject *pProject,
WaveTrack *pTrack, bool shiftDown, bool rightUp,
WaveTrack *pTrack, int ZoomKind,
const wxRect &rect, int zoomStart, int zoomEnd,
bool fixedMousePoint);