1 /**********************************************************************
2 
3 Audacity: A Digital Audio Editor
4 
5 Scrubbing.h
6 
7 Paul Licameli split from TrackPanel.cpp
8 
9 **********************************************************************/
10 
11 #ifndef __AUDACITY_SCRUBBING__
12 #define __AUDACITY_SCRUBBING__
13 
14 
15 
16 #include <vector>
17 #include <wx/longlong.h>
18 
19 #include "../../ScrubState.h" // for ScrubbingOptions
20 #include "ClientData.h" // to inherit
21 #include "Prefs.h" // to inherit
22 #include "../../widgets/Overlay.h" // to inherit
23 #include "../../commands/CommandContext.h"
24 #include "../../commands/CommandManager.h" // for MenuTable
25 #include "Identifier.h"
26 
27 class AudacityProject;
28 class TranslatableString;
29 
30 // Conditionally compile either a separate thead, or else use a timer in the main
31 // thread, to poll the mouse and update scrubbing speed and direction.  The advantage of
32 // a thread may be immunity to choppy scrubbing in case redrawing takes too much time.
33 #ifdef __WXGTK__
34 // Unfortunately some things the thread needs to do are not thread safe
35 #else
36 #define USE_SCRUB_THREAD
37 #endif
38 
39 // Scrub state object
40 class AUDACITY_DLL_API Scrubber final
41    : public wxEvtHandler
42    , public ClientData::Base
43    , private PrefsListener
44    , public std::enable_shared_from_this< Scrubber >
45 {
46 public:
47    static Scrubber &Get( AudacityProject &project );
48    static const Scrubber &Get( const AudacityProject &project );
49 
50    explicit
51    Scrubber(AudacityProject *project);
52    Scrubber( const Scrubber & ) PROHIBITED;
53    Scrubber &operator=( const Scrubber & ) PROHIBITED;
54    ~Scrubber();
55 
56    static bool ShouldScrubPinned();
57 
58    // Assume xx is relative to the left edge of TrackPanel!
59    void MarkScrubStart(wxCoord xx, bool smoothScrolling, bool seek);
60 
61    // Returns true iff the event should be considered consumed by this:
62    // Assume xx is relative to the left edge of TrackPanel!
63    bool MaybeStartScrubbing(wxCoord xx);
64    bool StartKeyboardScrubbing(double time0, bool backwards);
65    double GetKeyboardScrubbingSpeed();
66 
67    void ContinueScrubbingUI();
68    void ContinueScrubbingPoll();
69 
70    // This is meant to be called only from ProjectAudioManager
71    void StopScrubbing();
72 
GetScrubStartPosition()73    wxCoord GetScrubStartPosition() const
74    { return mScrubStartPosition; }
75 
WasSpeedPlaying()76    bool WasSpeedPlaying() const
77    { return mSpeedPlaying;}
IsSpeedPlaying()78    bool IsSpeedPlaying() const
79    { return IsScrubbing() && mSpeedPlaying; }
WasKeyboardScrubbing()80    bool WasKeyboardScrubbing() const
81    { return mKeyboardScrubbing; }
IsKeyboardScrubbing()82    bool IsKeyboardScrubbing() const
83    { return IsScrubbing() && mKeyboardScrubbing; }
SetBackwards(bool backwards)84    void SetBackwards(bool backwards)
85    { mBackwards = backwards;}
IsBackwards()86    bool IsBackwards() const
87    { return mBackwards;}
88    // True iff the user has clicked to start scrub and not yet stopped,
89    // but IsScrubbing() may yet be false
HasMark()90    bool HasMark() const
91    { return GetScrubStartPosition() >= 0; }
92    bool IsScrubbing() const;
93 
IsScrollScrubbing()94    bool IsScrollScrubbing() const // If true, implies HasMark()
95    { return mSmoothScrollingScrub; }
SetScrollScrubbing(bool value)96    void SetScrollScrubbing(bool value)
97    { mSmoothScrollingScrub = value; }
98 
99    bool ChoseSeeking() const;
SetMayDragToSeek(bool value)100    void SetMayDragToSeek( bool value ) { mMayDragToSeek = value; }
MayDragToSeek()101    bool MayDragToSeek() const { return mMayDragToSeek; }
102    bool TemporarilySeeks() const;
103    bool Seeks() const;
104    bool Scrubs() const;
105    bool ShowsBar() const;
106 
Cancel()107    void Cancel()
108    { mCancelled = true; }
109 
110    bool ShouldDrawScrubSpeed();
111    double FindScrubSpeed(bool seeking, double time) const;
GetMaxScrubSpeed()112    double GetMaxScrubSpeed() const { return mOptions.maxSpeed; }
113 
114    void HandleScrollWheel(int steps);
115 
116    // This returns the same as the enabled state of the menu items:
117    bool CanScrub() const;
118 
119    // For popup
120    void PopulatePopupMenu(wxMenu &menu);
121 
122    void OnScrubOrSeek(bool seek);
123    void OnScrub(const CommandContext&);
124    void OnSeek(const CommandContext&);
125    void OnToggleScrubRuler(const CommandContext&);
126 
127    void OnKeyboardScrubBackwards(const CommandContext&);
128    void OnKeyboardScrubForwards(const CommandContext&);
129    void DoKeyboardScrub(bool backwards, bool keyUp);
130 
131    // Convenience wrapper for the above
132    template<void (Scrubber::*pfn)(const CommandContext&)>
Thunk(wxCommandEvent &)133       void Thunk(wxCommandEvent &)
134          { (this->*pfn)(*mProject); }
135 
136    // A string to put in the leftmost part of the status bar
137    // when scrub or seek is in progress, or else empty.
138    const TranslatableString &GetUntranslatedStateString() const;
139    wxString StatusMessageForWave() const;
140 
141    void Pause(bool paused);
142    bool IsPaused() const;
143    void CheckMenuItems();
144 
145    bool IsTransportingPinned() const;
146 
SetSeekPress(bool value)147    void SetSeekPress( bool value ) { mScrubSeekPress = value; }
148 
149 private:
150    void UpdatePrefs() override;
151 
152    void StartPolling();
153    void StopPolling();
154    void DoScrub(bool seek);
155    void OnActivateOrDeactivateApp(wxActivateEvent & event);
156 
157 private:
158    int mScrubToken;
159    int mScrubSpeedDisplayCountdown;
160    wxCoord mScrubStartPosition;
161    wxCoord mLastScrubPosition {};
162    bool mScrubSeekPress {};
163    bool mSmoothScrollingScrub;
164 
165    bool mPaused{};
166    bool mSeeking {};
167    bool mSpeedPlaying{true};
168    bool mKeyboardScrubbing{};
169    bool mBackwards{};
170    bool mDragging {};
171 
172    bool mCancelled {};
173 
174 #ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
175    int mLogMaxScrubSpeed;
176 #endif
177 
178    AudacityProject *mProject;
179 
180    DECLARE_EVENT_TABLE()
181 
182 #ifdef USE_SCRUB_THREAD
183    // Course corrections in playback are done in a helper thread, unhindered by
184    // the complications of the main event dispatch loop
185    class ScrubPollerThread;
186    ScrubPollerThread *mpThread {};
187 #endif
188 
189    // Other periodic update of the UI must be done in the main thread,
190    // by this object which is driven by timer events.
191    class ScrubPoller;
192    std::unique_ptr<ScrubPoller> mPoller;
193 
194    ScrubbingOptions mOptions;
195    double mMaxSpeed { 1.0 };
196 
197    bool mShowScrubbing { false };
198    bool mMayDragToSeek{ false };
199 };
200 
201 #endif
202