mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-02 17:09:26 +02:00
... because that will be needed for elimination of some GetActiveProject() calls. Because some overrides need to find the focused track, but that may mutate the project by setting the focused track when it wasn't yet defined.
225 lines
6.3 KiB
C++
225 lines
6.3 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
ZoomHandle.cpp
|
|
|
|
Paul Licameli split from TrackPanel.cpp
|
|
|
|
**********************************************************************/
|
|
|
|
#include "../../Audacity.h"
|
|
#include "ZoomHandle.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include <wx/dc.h>
|
|
#include <wx/event.h>
|
|
#include <wx/gdicmn.h>
|
|
|
|
#include "../../HitTestResult.h"
|
|
#include "../../RefreshCode.h"
|
|
#include "../../TrackArtist.h"
|
|
#include "../../TrackPanelDrawingContext.h"
|
|
#include "../../TrackPanelMouseEvent.h"
|
|
#include "../../ViewInfo.h"
|
|
#include "../../../images/Cursors.h"
|
|
|
|
/// This class takes care of our different zoom
|
|
/// possibilities. It is possible for a user to just
|
|
/// "zoom in" or "zoom out," but it is also possible
|
|
/// for a user to drag and select an area that he
|
|
/// or she wants to be zoomed in on. We use mZoomStart
|
|
/// and mZoomEnd to track the beginning and end of such
|
|
/// a zoom area. Note that the ViewInfo
|
|
/// actually keeps track of our zoom constant,
|
|
/// so we achieve zooming by altering the zoom constant
|
|
/// and forcing a refresh.
|
|
|
|
ZoomHandle::ZoomHandle()
|
|
{}
|
|
|
|
HitTestPreview ZoomHandle::HitPreview
|
|
(const wxMouseState &state, const AudacityProject *WXUNUSED(pProject))
|
|
{
|
|
static auto zoomInCursor =
|
|
::MakeCursor(wxCURSOR_MAGNIFIER, ZoomInCursorXpm, 19, 15);
|
|
static auto zoomOutCursor =
|
|
::MakeCursor(wxCURSOR_MAGNIFIER, ZoomOutCursorXpm, 19, 15);
|
|
TranslatableString message;
|
|
// TODO: Why not mention middle click to zoom normal on Windows too?
|
|
#if defined( __WXMAC__ )
|
|
message = XO("Click to Zoom In, Shift-Click to Zoom Out");
|
|
#elif defined( __WXMSW__ )
|
|
message = XO("Drag to Zoom Into Region, Right-Click to Zoom Out");
|
|
#elif defined( __WXGTK__ )
|
|
message = XO("Left=Zoom In, Right=Zoom Out, Middle=Normal");
|
|
#endif
|
|
return {
|
|
message,
|
|
(state.ShiftDown() ? &*zoomOutCursor : &*zoomInCursor)
|
|
};
|
|
}
|
|
|
|
UIHandlePtr ZoomHandle::HitAnywhere
|
|
(std::weak_ptr<ZoomHandle> &holder)
|
|
{
|
|
auto result = std::make_shared<ZoomHandle>();
|
|
result = AssignUIHandlePtr(holder, result);
|
|
return result;
|
|
}
|
|
|
|
UIHandlePtr ZoomHandle::HitTest
|
|
(std::weak_ptr<ZoomHandle> &holder,
|
|
const wxMouseState &state)
|
|
{
|
|
if (state.ButtonIsDown(wxMOUSE_BTN_RIGHT))
|
|
return HitAnywhere(holder);
|
|
else
|
|
return {};
|
|
}
|
|
|
|
ZoomHandle::~ZoomHandle()
|
|
{
|
|
}
|
|
|
|
UIHandle::Result ZoomHandle::Click
|
|
(const TrackPanelMouseEvent &evt, AudacityProject *)
|
|
{
|
|
const wxMouseEvent &event = evt.event;
|
|
if (event.ButtonDown() || event.LeftDClick()) {
|
|
/// Zoom button down, record the position.
|
|
mZoomStart = event.m_x;
|
|
mZoomEnd = event.m_x;
|
|
mRect = evt.rect;
|
|
}
|
|
return RefreshCode::RefreshNone;
|
|
}
|
|
|
|
UIHandle::Result ZoomHandle::Drag
|
|
(const TrackPanelMouseEvent &evt, AudacityProject *)
|
|
{
|
|
const wxMouseEvent &event = evt.event;
|
|
const int left = mRect.GetLeft();
|
|
const int right = mRect.GetRight();
|
|
|
|
mZoomEnd = event.m_x;
|
|
|
|
if (event.m_x < left) {
|
|
mZoomEnd = left;
|
|
}
|
|
else if (event.m_x > right) {
|
|
mZoomEnd = right;
|
|
}
|
|
|
|
// Refresh tracks ALWAYS. Even if IsDragZooming() becomes false, make the
|
|
// dashed lines disappear. -- PRL
|
|
return RefreshCode::RefreshAll; // (IsDragZooming() ? RefreshAllTracks : RefreshNone),
|
|
}
|
|
|
|
HitTestPreview ZoomHandle::Preview
|
|
(const TrackPanelMouseState &st, AudacityProject *pProject)
|
|
{
|
|
return HitPreview(st.state, pProject);
|
|
}
|
|
|
|
UIHandle::Result ZoomHandle::Release
|
|
(const TrackPanelMouseEvent &evt, AudacityProject *pProject,
|
|
wxWindow *)
|
|
{
|
|
const wxMouseEvent &event = evt.event;
|
|
auto &viewInfo = ViewInfo::Get( *pProject );
|
|
if (mZoomEnd < mZoomStart)
|
|
std::swap(mZoomStart, mZoomEnd);
|
|
|
|
const int trackLeftEdge = mRect.x;
|
|
if (IsDragZooming())
|
|
{
|
|
/// This actually sets the Zoom value when you're done doing
|
|
/// a drag zoom.
|
|
double left = viewInfo.PositionToTime(mZoomStart, trackLeftEdge);
|
|
double right = viewInfo.PositionToTime(mZoomEnd, trackLeftEdge);
|
|
|
|
double multiplier =
|
|
(viewInfo.PositionToTime(mRect.width) - viewInfo.PositionToTime(0)) /
|
|
(right - left);
|
|
if (event.ShiftDown())
|
|
multiplier = 1.0 / multiplier;
|
|
|
|
viewInfo.ZoomBy(multiplier);
|
|
|
|
viewInfo.h = left;
|
|
}
|
|
else
|
|
{
|
|
/// This handles normal Zoom In/Out, if you just clicked;
|
|
/// IOW, if you were NOT dragging to zoom an area.
|
|
/// \todo MAGIC NUMBER: We've got several in this method.
|
|
const double center_h =
|
|
viewInfo.PositionToTime(event.m_x, trackLeftEdge);
|
|
|
|
const double multiplier =
|
|
(event.RightUp() || event.RightDClick() || event.ShiftDown())
|
|
? 0.5 : 2.0;
|
|
viewInfo.ZoomBy(multiplier);
|
|
|
|
if (event.MiddleUp() || event.MiddleDClick())
|
|
viewInfo.SetZoom(ZoomInfo::GetDefaultZoom()); // AS: Reset zoom.
|
|
|
|
const double new_center_h =
|
|
viewInfo.PositionToTime(event.m_x, trackLeftEdge);
|
|
|
|
viewInfo.h += (center_h - new_center_h);
|
|
}
|
|
|
|
mZoomEnd = mZoomStart = 0;
|
|
|
|
using namespace RefreshCode;
|
|
return RefreshAll | FixScrollbars;
|
|
}
|
|
|
|
UIHandle::Result ZoomHandle::Cancel(AudacityProject*)
|
|
{
|
|
// Cancel is implemented! And there is no initial state to restore,
|
|
// so just return a code.
|
|
return RefreshCode::RefreshAll;
|
|
}
|
|
|
|
void ZoomHandle::Draw(
|
|
TrackPanelDrawingContext &context,
|
|
const wxRect &rect, unsigned iPass )
|
|
{
|
|
if ( iPass == TrackArtist::PassZooming &&
|
|
// PRL: Draw dashed lines only if we would zoom in
|
|
// for button up.
|
|
IsDragZooming() ) {
|
|
auto &dc = context.dc;
|
|
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
|
dc.SetPen(*wxBLACK_DASHED_PEN);
|
|
// Make the top and bottom of the dashed rectangle disappear out of
|
|
// bounds, so that you only see vertical dashed lines.
|
|
dc.DrawRectangle( {
|
|
std::min(mZoomStart, mZoomEnd),
|
|
rect.y - 1,
|
|
1 + abs(mZoomEnd - mZoomStart),
|
|
rect.height + 2
|
|
} );
|
|
}
|
|
}
|
|
|
|
wxRect ZoomHandle::DrawingArea(
|
|
TrackPanelDrawingContext &,
|
|
const wxRect &rect, const wxRect &panelRect, unsigned iPass )
|
|
{
|
|
if ( iPass == TrackArtist::PassZooming )
|
|
return MaximizeHeight( rect, panelRect );
|
|
else
|
|
return rect;
|
|
}
|
|
|
|
bool ZoomHandle::IsDragZooming() const
|
|
{
|
|
const int DragThreshold = 3;// Anything over 3 pixels is a drag, else a click.
|
|
return (abs(mZoomEnd - mZoomStart) > DragThreshold);
|
|
}
|