Audacity  3.0.3
ScrubUI.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  ScrubUI.cpp
6 
7  Paul Licameli split from Scrubbing.cpp
8 
9  **********************************************************************/
10 
11 #include "ScrubUI.h"
12 
13 #include "Scrubbing.h"
14 #include "../../widgets/Overlay.h"
15 #include "ClientData.h"
16 #include "../../AdornedRulerPanel.h"
17 #include "Project.h"
18 #include "../../ProjectWindow.h"
19 #include "../../ProjectWindows.h"
20 #include "../../TrackPanel.h"
21 
22 #include <wx/dcclient.h>
23 #include <wx/event.h>
24 #include <wx/windowptr.h>
25 
27 // class ScrubbingOverlay is responsible for drawing the speed numbers
28 
29 // Specialist in drawing the scrub speed, and listening for certain events
30 class ScrubbingOverlay final
31  : public wxEvtHandler
32  , public Overlay
33  , public ClientData::Base
34 {
35 public:
36  explicit
38 
39 private:
40  unsigned SequenceNumber() const override;
41  std::pair<wxRect, bool> DoGetRectangle(wxSize size) override;
42  void Draw(OverlayPanel &panel, wxDC &dc) override;
43 
44  void OnTimer(wxCommandEvent &event);
45 
46  const Scrubber &GetScrubber() const;
48 
50 
53 };
54 
56  : mProject(project)
57  , mLastScrubRect()
58  , mNextScrubRect()
59  , mLastScrubSpeedText()
60  , mNextScrubSpeedText()
61 {
62  mProject->Bind(EVT_TRACK_PANEL_TIMER,
64  this);
65 }
66 
68 {
69  return 40;
70 }
71 
72 std::pair<wxRect, bool> ScrubbingOverlay::DoGetRectangle(wxSize)
73 {
74  wxRect rect(mLastScrubRect);
75  const bool outdated =
77  (!mLastScrubRect.IsEmpty() && !GetScrubber().ShouldDrawScrubSpeed()) ||
79  return std::make_pair(
80  rect,
81  outdated
82  );
83 }
84 
86 {
89 
90  Scrubber &scrubber = GetScrubber();
91  if (!scrubber.ShouldDrawScrubSpeed())
92  return;
93 
94  static const wxFont labelFont(24, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
95  dc.SetFont(labelFont);
96 
97  // These two colors were previously saturated red and green. However
98  // we have a rule to try to only use red for reserved purposes of
99  // (a) Recording
100  // (b) Error alerts
101  // So they were changed to 'orange' and 'lime'.
102  static const wxColour clrNoScroll(215, 162, 0), clrScroll(0, 204, 153);
103  if (scrubber.IsScrollScrubbing())
104  dc.SetTextForeground(clrScroll);
105  else
106  dc.SetTextForeground(clrNoScroll);
107 
108  dc.DrawText(mLastScrubSpeedText, mLastScrubRect.GetX(), mLastScrubRect.GetY());
109 }
110 
111 void ScrubbingOverlay::OnTimer(wxCommandEvent &event)
112 {
113  // Let other listeners get the notification
114  event.Skip();
115 
116  Scrubber &scrubber = GetScrubber();
117  const auto isScrubbing = scrubber.IsScrubbing();
119  auto position = ::wxGetMousePosition();
120 
121  if (scrubber.IsSpeedPlaying() || scrubber.IsKeyboardScrubbing())
122  return;
123 
124  {
125  if(scrubber.HasMark()) {
126  auto xx = ruler.ScreenToClient(position).x;
127  ruler.UpdateQuickPlayPos( xx, false );
128 
129  if (!isScrubbing)
130  // Really start scrub if motion is far enough
131  scrubber.MaybeStartScrubbing(xx);
132  }
133 
134  if (!isScrubbing) {
135  mNextScrubRect = wxRect();
136  return;
137  }
138  else
139  ruler.DrawBothOverlays();
140  }
141 
142  if (!scrubber.ShouldDrawScrubSpeed()) {
143  mNextScrubRect = wxRect();
144  }
145  else {
146  auto &trackPanel = GetProjectPanel( *mProject );
147  auto &viewInfo = ViewInfo::Get( *mProject );
148  int panelWidth, panelHeight;
149  trackPanel.GetSize(&panelWidth, &panelHeight);
150 
151  // Where's the mouse?
152  position = trackPanel.ScreenToClient(position);
153 
154  const bool seeking = scrubber.Seeks() || scrubber.TemporarilySeeks();
155 
156  // Find the text
157  const double maxScrubSpeed = GetScrubber().GetMaxScrubSpeed();
158  const double speed =
159  scrubber.IsScrollScrubbing()
160  ? scrubber.FindScrubSpeed( seeking,
162  .PositionToTime(position.x, viewInfo.GetLeftOffset()))
163  : maxScrubSpeed;
164 
165  const wxChar *format =
166  scrubber.IsScrollScrubbing()
167  ? seeking
168  ? wxT("%+.2fX")
169  : wxT("%+.2f")
170  : wxT("%.2f");
171 
172  mNextScrubSpeedText = wxString::Format(format, speed);
173 
174  // Find the origin for drawing text
175  wxCoord width, height;
176  {
177  wxClientDC dc( &trackPanel );
178  static const wxFont labelFont(24, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
179  dc.SetFont(labelFont);
180  dc.GetTextExtent(mNextScrubSpeedText, &width, &height);
181  }
182  const auto xx =
183  std::max(0, std::min(panelWidth - width, position.x - width / 2));
184 
185  // Put the text above the cursor, if it fits.
186  enum { offset = 20 };
187  auto yy = position.y - height + offset;
188  if (yy < 0)
189  yy += height + 2 * offset;
190  yy = std::max(0, std::min(panelHeight - height, yy));
191 
192  mNextScrubRect = wxRect(xx, yy, width, height);
193  }
194 }
195 
197 {
198  return Scrubber::Get( *mProject );
199 }
200 
202 {
203  return Scrubber::Get( *mProject );
204 }
205 
207  []( AudacityProject &parent ){
208  auto result = std::make_shared< ScrubbingOverlay >( &parent );
209  TrackPanel::Get( parent ).AddOverlay( result );
210  return result;
211  }
212 };
213 
215 // class ScrubForwarder intercepts some mouse events of the main window
216 
217 // I need this because I can't push the scrubber as an event handler
218 // in two places at once.
220  : public wxEvtHandler
221  , public ClientData::Base
222 {
224  : mProject{ project }
225  {
226  mWindow = &ProjectWindow::Get( project );
227  if ( mWindow )
228  mWindow->PushEventHandler( this );
229  mRuler = &AdornedRulerPanel::Get( project );
230  mScrubber = Scrubber::Get( project ).shared_from_this();
231  }
232 
234  {
235  if ( mWindow )
236  mWindow->PopEventHandler();
237  }
238 
240  wxWindowPtr<wxWindow> mWindow;
241  wxWeakRef<AdornedRulerPanel> mRuler;
242  std::weak_ptr<Scrubber> mScrubber;
243 
244  void OnMouse(wxMouseEvent &event);
245  DECLARE_EVENT_TABLE()
246 };
247 
248 void ScrubForwarder::OnMouse(wxMouseEvent &event)
249 {
250  auto pScrubber = mScrubber.lock();
251  if ( !pScrubber || !mRuler ) {
252  event.Skip();
253  return;
254  }
255 
256  auto &scrubber = *pScrubber;
257 
258  auto &ruler = *mRuler;
259  const auto &state = ::wxGetMouseState();
260  const auto &position = state.GetPosition();
261  scrubber.SetMayDragToSeek(
262  ruler.GetScreenRect().Contains(position) );
263 
264  /*
265  auto trackPanel = mProject->GetTrackPanel();
266  if (trackPanel &&
267  trackPanel->GetScreenRect().Contains(position))
268  return true;
269  */
270 
271  //auto ruler = scrubber.mProject->GetRulerPanel();
272  auto isScrubbing = scrubber.IsScrubbing();
273  if (isScrubbing && !event.HasAnyModifiers()) {
274  if(event.LeftDown() && scrubber.MayDragToSeek()) {
275  // This event handler may catch mouse transitions that are missed
276  // by the polling of mouse state by the timer.
277  scrubber.SetSeekPress( true );
278  }
279  else if (event.m_wheelRotation) {
280  double steps = event.m_wheelRotation /
281  (event.m_wheelDelta > 0 ? (double)event.m_wheelDelta : 120.0);
282  scrubber.HandleScrollWheel(steps);
283  }
284  else
285  event.Skip();
286  }
287  else
288  event.Skip();
289 }
290 
291 BEGIN_EVENT_TABLE(ScrubForwarder, wxEvtHandler)
292  EVT_MOUSE_EVENTS(ScrubForwarder::OnMouse)
294 
295 static const AudacityProject::AttachedObjects::RegisteredFactory sForwarderKey{
296  []( AudacityProject &parent ){
297  return std::make_shared< ScrubForwarder >( parent );
298  }
299 };
ScrubbingOverlay::SequenceNumber
unsigned SequenceNumber() const override
This number determines an ordering of overlays, so that those with higher numbers overpaint those wit...
Definition: ScrubUI.cpp:67
ViewInfo::Get
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:161
ScrubForwarder::mWindow
wxWindowPtr< wxWindow > mWindow
Definition: ScrubUI.cpp:240
ScrubbingOverlay::DoGetRectangle
std::pair< wxRect, bool > DoGetRectangle(wxSize size) override
Definition: ScrubUI.cpp:72
Scrubber::Get
static Scrubber & Get(AudacityProject &project)
Definition: Scrubbing.cpp:200
ScrubForwarder
Definition: ScrubUI.cpp:222
Project.h
ScrubbingOverlay::Draw
void Draw(OverlayPanel &panel, wxDC &dc) override
Definition: ScrubUI.cpp:85
sOverlayKey
static const AudacityProject::AttachedObjects::RegisteredFactory sOverlayKey
Definition: ScrubUI.cpp:206
ScrubForwarder::mRuler
wxWeakRef< AdornedRulerPanel > mRuler
Definition: ScrubUI.cpp:241
Scrubber::MaybeStartScrubbing
bool MaybeStartScrubbing(wxCoord xx)
Overlay
Definition: Overlay.h:94
TrackPanel::Get
static TrackPanel & Get(AudacityProject &project)
Definition: TrackPanel.cpp:227
ClientData::Base
A convenient default parameter for class template Site.
Definition: ClientData.h:28
Scrubber::Seeks
bool Seeks() const
ScrubbingOverlay::mLastScrubRect
wxRect mLastScrubRect
Definition: ScrubUI.cpp:51
ProjectWindow::Get
static ProjectWindow & Get(AudacityProject &project)
Definition: ProjectWindow.cpp:535
ClientData.h
Utility ClientData::Site to register hooks into a host class that attach client data.
Scrubber
Definition: Scrubbing.h:45
Scrubber::HasMark
bool HasMark() const
Definition: Scrubbing.h:91
anonymous_namespace{TimeTrackVRulerControls.cpp}::ruler
Ruler & ruler()
Definition: TimeTrackVRulerControls.cpp:34
ScrubbingOverlay::OnTimer
void OnTimer(wxCommandEvent &event)
Definition: ScrubUI.cpp:111
ClientData::Site::RegisteredFactory
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:266
ScrubbingOverlay::mProject
AudacityProject * mProject
Definition: ScrubUI.cpp:49
ScrubForwarder::ScrubForwarder
ScrubForwarder(AudacityProject &project)
Definition: ScrubUI.cpp:223
AdornedRulerPanel::Get
static AdornedRulerPanel & Get(AudacityProject &project)
Definition: AdornedRulerPanel.cpp:874
Scrubber::IsScrollScrubbing
bool IsScrollScrubbing() const
Definition: Scrubbing.h:95
Scrubber::IsSpeedPlaying
bool IsSpeedPlaying() const
Definition: Scrubbing.h:79
Scrubber::IsKeyboardScrubbing
bool IsKeyboardScrubbing() const
Definition: Scrubbing.h:83
format
int format
Definition: ExportPCM.cpp:56
Scrubbing.h
Scrubber::TemporarilySeeks
bool TemporarilySeeks() const
ScrubbingOverlay::mLastScrubSpeedText
wxString mLastScrubSpeedText
Definition: ScrubUI.cpp:52
ScrubbingOverlay::mNextScrubSpeedText
wxString mNextScrubSpeedText
Definition: ScrubUI.cpp:52
Scrubber::GetMaxScrubSpeed
double GetMaxScrubSpeed() const
Definition: Scrubbing.h:113
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
ScrubForwarder::mScrubber
std::weak_ptr< Scrubber > mScrubber
Definition: ScrubUI.cpp:242
Scrubber::ShouldDrawScrubSpeed
bool ShouldDrawScrubSpeed()
ScrubForwarder::mProject
AudacityProject & mProject
Definition: ScrubUI.cpp:239
Scrubber::FindScrubSpeed
double FindScrubSpeed(bool seeking, double time) const
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:92
Scrubber::IsScrubbing
bool IsScrubbing() const
ScrubForwarder::~ScrubForwarder
~ScrubForwarder()
Definition: ScrubUI.cpp:233
OverlayPanel
Definition: OverlayPanel.h:18
OverlayPanel::AddOverlay
void AddOverlay(const std::weak_ptr< Overlay > &pOverlay)
Definition: OverlayPanel.cpp:24
ScrubbingOverlay::mNextScrubRect
wxRect mNextScrubRect
Definition: ScrubUI.cpp:51
ScrubbingOverlay
Definition: ScrubUI.cpp:34
ScrubUI.h
ScrubForwarder::OnMouse
void OnMouse(wxMouseEvent &event)
Definition: ScrubUI.cpp:248
GetProjectPanel
AUDACITY_DLL_API wxWindow & GetProjectPanel(AudacityProject &project)
Get the main sub-window of the project frame that displays track data.
Definition: ProjectWindows.cpp:49
ScrubbingOverlay::ScrubbingOverlay
ScrubbingOverlay(AudacityProject *project)
Definition: ScrubUI.cpp:55
sForwarderKey
static const AudacityProject::AttachedObjects::RegisteredFactory sForwarderKey
Definition: ScrubUI.cpp:295
END_EVENT_TABLE
END_EVENT_TABLE()
ScrubbingOverlay::GetScrubber
const Scrubber & GetScrubber() const
Definition: ScrubUI.cpp:196