diff --git a/src/Menus.cpp b/src/Menus.cpp index a4a7e42e5..22e6d73eb 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -2384,7 +2384,6 @@ void AudacityProject::OnTogglePinnedHead() auto ruler = GetRulerPanel(); if (ruler) // Update button image - ruler->UpdateButtonStates(); auto &scrubber = GetScrubber(); diff --git a/src/Project.cpp b/src/Project.cpp index 1b8a42a8f..e52a0805d 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -890,7 +890,7 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, mRuler = safenew AdornedRulerPanel( this, wxID_ANY, wxDefaultPosition, - wxSize( -1, AdornedRulerPanel::GetRulerHeight() ), + wxSize( -1, AdornedRulerPanel::GetRulerHeight(false) ), &mViewInfo ); // diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index 79a947747..3a8b6e4c3 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -33,9 +33,6 @@ Paul Licameli split from TrackPanel.cpp #include -// Conditional compilation switch for making scrub menu items checkable -#define CHECKABLE_SCRUB_MENU_ITEMS - enum { // PRL: // Mouse must move at least this far to distinguish ctrl-drag to scrub @@ -221,24 +218,27 @@ namespace { wxString status; void (Scrubber::*memFn)(wxCommandEvent&); bool seek; + bool (Scrubber::*StatusTest)() const; const wxString &GetStatus() const { return status; } } menuItems[] = { /* i18n-hint: These commands assist the user in finding a sound by ear. ... "Scrubbing" is variable-speed playback, ... "Seeking" is normal speed playback but with skips, ... - "Scrolling" keeps the playback position at a fixed place on screen while the waveform moves */ { wxT("Scrub"), XO("&Scrub"), XO("Scrubbing"), - &Scrubber::OnScrub, false }, + &Scrubber::OnScrub, false, &Scrubber::Scrubs }, { wxT("Seek"), XO("See&k"), XO("Seeking"), - &Scrubber::OnSeek, true }, + &Scrubber::OnSeek, true, &Scrubber::Seeks }, + { wxT("StartScrubSeek"), XO("Star&t"), XO(""), + &Scrubber::OnStart, true, nullptr }, }; - enum { nMenuItems = sizeof(menuItems) / sizeof(*menuItems) }; + enum { nMenuItems = sizeof(menuItems) / sizeof(*menuItems), StartMenuItem = 2 }; + // This never finds the last item: inline const MenuItem &FindMenuItem(bool seek) { return *std::find_if(menuItems, menuItems + nMenuItems, @@ -252,16 +252,13 @@ namespace { void Scrubber::MarkScrubStart( // Assume xx is relative to the left edge of TrackPanel! - wxCoord xx, bool smoothScrolling, bool alwaysSeeking + wxCoord xx, bool smoothScrolling ) { - UncheckAllMenuItems(); - // Don't actually start scrubbing, but collect some information // needed for the decision to start scrubbing later when handling // drag events. mSmoothScrollingScrub = smoothScrolling; - mAlwaysSeeking = alwaysSeeking; ControlToolBar * const ctb = mProject->GetControlToolBar(); @@ -278,8 +275,6 @@ void Scrubber::MarkScrubStart( mScrubStartPosition = xx; mOptions.startClockTimeMillis = ::wxGetLocalTimeMillis(); - - CheckMenuItem(); } #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT @@ -468,12 +463,12 @@ void Scrubber::ContinueScrubbingUI() { // Show the correct status for seeking. - bool backup = mAlwaysSeeking; - mAlwaysSeeking = seek; + bool backup = mSeeking; + mSeeking = seek; const auto ctb = mProject->GetControlToolBar(); if (ctb) ctb->UpdateStatusBar(mProject); - mAlwaysSeeking = backup; + mSeeking = backup; } if (seek) @@ -498,8 +493,6 @@ void Scrubber::StopScrubbing() mPoller->Stop(); - UncheckAllMenuItems(); - mScrubStartPosition = -1; mDragging = false; @@ -770,13 +763,12 @@ Scrubber &ScrubbingOverlay::GetScrubber() bool Scrubber::PollIsSeeking() { - return mDragging || (mAlwaysSeeking || ::wxGetMouseState().LeftIsDown()); + return mDragging || (mSeeking || ::wxGetMouseState().LeftIsDown()); } -void Scrubber::DoScrub(bool seek) +void Scrubber::DoScrub() { const bool wasScrubbing = IsScrubbing(); - const bool match = (seek == mAlwaysSeeking); const bool scroll = PlaybackPrefs::GetPinnedHeadPreference(); if (!wasScrubbing) { auto tp = mProject->GetTrackPanel(); @@ -788,33 +780,43 @@ void Scrubber::DoScrub(bool seek) const auto offset = tp->GetLeftOffset(); xx = (std::max(offset, std::min(offset + width - 1, xx))); - MarkScrubStart(xx, scroll, seek); + MarkScrubStart(xx, scroll); } - else if(!match) { - mSmoothScrollingScrub = scroll; - mAlwaysSeeking = seek; - UncheckAllMenuItems(); - CheckMenuItem(); +} +void Scrubber::OnScrubOrSeek(bool &toToggle, bool &other) +{ + toToggle = !toToggle; + if (toToggle) + other = false; + + if (HasStartedScrubbing()) { // Show the correct status. const auto ctb = mProject->GetControlToolBar(); ctb->UpdateStatusBar(mProject); } - else { - // This will call back to Scrubber::StopScrubbing - const auto ctb = mProject->GetControlToolBar(); - ctb->StopPlaying(); - } + + auto ruler = mProject->GetRulerPanel(); + if (ruler) + // Update button images + ruler->UpdateButtonStates(); + + CheckMenuItem(); } void Scrubber::OnScrub(wxCommandEvent&) { - DoScrub(false); + OnScrubOrSeek(mScrubbing, mSeeking); } void Scrubber::OnSeek(wxCommandEvent&) { - DoScrub(true); + OnScrubOrSeek(mSeeking, mScrubbing); +} + +void Scrubber::OnStart(wxCommandEvent&) +{ + DoScrub(); } enum { CMD_ID = 8000 }; @@ -828,14 +830,14 @@ BEGIN_EVENT_TABLE(Scrubber::Forwarder, wxEvtHandler) EVT_MOUSE_EVENTS(Scrubber::Forwarder::OnMouse) END_EVENT_TABLE() -static_assert(nMenuItems == 2, "wrong number of items"); +static_assert(nMenuItems == 3, "wrong number of items"); const wxString &Scrubber::GetUntranslatedStateString() const { static wxString empty; if (HasStartedScrubbing()) { - auto &item = FindMenuItem(mAlwaysSeeking); + auto &item = FindMenuItem(mSeeking); return item.status; } else @@ -846,15 +848,19 @@ std::vector Scrubber::GetAllUntranslatedStatusStrings() { using namespace std; vector results; - transform(menuItems, menuItems + nMenuItems, back_inserter(results), - mem_fun_ref(&MenuItem::GetStatus)); + for (const auto &item : menuItems) { + const auto &status = item.GetStatus(); + if (!status.empty()) + results.push_back(status); + } return move(results); } bool Scrubber::CanScrub() const { + // Return the enabled state for the menu item that really launches the scrub or seek. auto cm = mProject->GetCommandManager(); - return cm->GetEnabled(menuItems[0].name); + return cm->GetEnabled(menuItems[StartMenuItem].name); } void Scrubber::AddMenuItems() @@ -865,15 +871,14 @@ void Scrubber::AddMenuItems() cm->BeginSubMenu(_("Scru&bbing")); for (const auto &item : menuItems) { -#ifdef CHECKABLE_SCRUB_MENU_ITEMS - cm->AddCheck(item.name, wxGetTranslation(item.label), - FNT(Scrubber, this, item.memFn), - false, flags, mask); -#else - cm->AddItem(item.name, wxGetTranslation(item.label), - FNT(Scrubber, this, item.memFn), - flags, mask); -#endif + if (!item.GetStatus().empty()) + cm->AddCheck(item.name, wxGetTranslation(item.label), + FNT(Scrubber, this, item.memFn), + false, flags, mask); + else + cm->AddItem(item.name, wxGetTranslation(item.label), + FNT(Scrubber, this, item.memFn), + flags, mask); } cm->EndSubMenu(); CheckMenuItem(); @@ -885,40 +890,23 @@ void Scrubber::PopulateMenu(wxMenu &menu) auto cm = mProject->GetCommandManager(); const MenuItem *checkedItem = HasStartedScrubbing() - ? &FindMenuItem(mAlwaysSeeking) + ? &FindMenuItem(mSeeking) : nullptr; for (const auto &item : menuItems) { if (cm->GetEnabled(item.name)) { -#ifdef CHECKABLE_SCRUB_MENU_ITEMS menu.AppendCheckItem(id, item.label); if(&item == checkedItem) menu.FindItem(id)->Check(); -#else - menu.Append(id, item.label); -#endif } ++id; } } -void Scrubber::UncheckAllMenuItems() -{ -#ifdef CHECKABLE_SCRUB_MENU_ITEMS - auto cm = mProject->GetCommandManager(); - for (const auto &item : menuItems) - cm->Check(item.name, false); -#endif -} - void Scrubber::CheckMenuItem() { -#ifdef CHECKABLE_SCRUB_MENU_ITEMS - if(HasStartedScrubbing()) { - auto cm = mProject->GetCommandManager(); - auto item = FindMenuItem(mAlwaysSeeking); - cm->Check(item.name, true); - } -#endif + auto cm = mProject->GetCommandManager(); + cm->Check(menuItems[0].name, mScrubbing); + cm->Check(menuItems[1].name, mSeeking); } #endif diff --git a/src/tracks/ui/Scrubbing.h b/src/tracks/ui/Scrubbing.h index 930d48751..fc73b5a6e 100644 --- a/src/tracks/ui/Scrubbing.h +++ b/src/tracks/ui/Scrubbing.h @@ -71,11 +71,7 @@ public: ~Scrubber(); // Assume xx is relative to the left edge of TrackPanel! - void MarkScrubStart( - wxCoord xx, bool smoothScrolling, - bool alwaysSeeking // if false, can switch seeking or scrubbing - // by mouse button state - ); + void MarkScrubStart(wxCoord xx, bool smoothScrolling); // Returns true iff the event should be considered consumed by this: // Assume xx is relative to the left edge of TrackPanel! @@ -101,8 +97,11 @@ public: void SetScrollScrubbing(bool value) { mSmoothScrollingScrub = value; } - bool IsAlwaysSeeking() const - { return mAlwaysSeeking; } + bool Seeks() const + { return mSeeking; } + + bool Scrubs() const + { return mScrubbing; } bool ShouldDrawScrubSpeed(); double FindScrubSpeed(bool seeking, double time) const; @@ -120,10 +119,13 @@ public: // For popup void PopulateMenu(wxMenu &menu); + void OnScrubOrSeek(bool &toToggle, bool &other); void OnScrub(wxCommandEvent&); void OnSeek(wxCommandEvent&); + void OnStart(wxCommandEvent&); - // A string to put in the leftmost part of the status bar. + // A string to put in the leftmost part of the status bar + // when scrub or seek is in progress, or else empty. const wxString &GetUntranslatedStateString() const; // All possible status strings. @@ -133,9 +135,8 @@ public: bool IsPaused() const; private: - void DoScrub(bool seek); + void DoScrub(); void OnActivateOrDeactivateApp(wxActivateEvent & event); - void UncheckAllMenuItems(); void CheckMenuItem(); // I need this because I can't push the scrubber as an event handler @@ -158,7 +159,12 @@ private: wxCoord mLastScrubPosition {}; bool mScrubSeekPress; bool mSmoothScrollingScrub; - bool mAlwaysSeeking {}; + + // These hold the three-way choice among click-to-scrub, click-to-seek, or disabled. + // Not both true. + bool mScrubbing {}; + bool mSeeking {}; + bool mDragging {}; #ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL diff --git a/src/widgets/Ruler.cpp b/src/widgets/Ruler.cpp index a28e84912..125587912 100644 --- a/src/widgets/Ruler.cpp +++ b/src/widgets/Ruler.cpp @@ -1918,8 +1918,8 @@ enum { OnLockPlayRegionID, OnTogglePinnedStateID, - - OnShowHideScrubbingID, + OnScrubID, + OnSeekID, }; BEGIN_EVENT_TABLE(AdornedRulerPanel, OverlayPanel) @@ -1935,15 +1935,18 @@ BEGIN_EVENT_TABLE(AdornedRulerPanel, OverlayPanel) EVT_MENU(OnAutoScrollID, AdornedRulerPanel::OnAutoScroll) EVT_MENU(OnLockPlayRegionID, AdornedRulerPanel::OnLockPlayRegion) - // Scrub bar menu commands - EVT_MENU(OnShowHideScrubbingID, AdornedRulerPanel::OnToggleScrubbing) - // Pop up menus on Windows EVT_CONTEXT_MENU(AdornedRulerPanel::OnContextMenu) EVT_COMMAND( OnTogglePinnedStateID, - wxEVT_COMMAND_BUTTON_CLICKED, - AdornedRulerPanel::OnTogglePinnedState ) + wxEVT_COMMAND_BUTTON_CLICKED, + AdornedRulerPanel::OnTogglePinnedState ) + EVT_COMMAND( OnScrubID, + wxEVT_COMMAND_BUTTON_CLICKED, + AdornedRulerPanel::OnScrub ) + EVT_COMMAND( OnSeekID, + wxEVT_COMMAND_BUTTON_CLICKED, + AdornedRulerPanel::OnSeek ) END_EVENT_TABLE() @@ -2026,6 +2029,7 @@ AdornedRulerPanel::~AdornedRulerPanel() this); } +#if 0 namespace { static const wxChar *scrubEnabledPrefName = wxT("/QuickPlay/ScrubbingEnabled"); @@ -2041,6 +2045,7 @@ namespace { gPrefs->Write(scrubEnabledPrefName, value); } } +#endif void AdornedRulerPanel::UpdatePrefs() { @@ -2058,7 +2063,7 @@ void AdornedRulerPanel::UpdatePrefs() #endif #endif - mShowScrubbing = ReadScrubEnabledPref(); + // mShowScrubbing = ReadScrubEnabledPref(); // Affected by the last UpdateRects(); @@ -2088,9 +2093,10 @@ void AdornedRulerPanel::ReCreateButtons() // Make the short row of time ruler pushbottons. // Don't bother with sizers. Their sizes and positions are fixed. - wxPoint position{ FocusBorderLeft, FocusBorderTop }; + wxPoint position{ FocusBorderLeft, 0 }; size_t iButton = 0; - const auto size = theTheme.ImageSize( bmpRecoloredUpSmall ); + auto size = theTheme.ImageSize( bmpRecoloredUpSmall ); + size.y = std::min(size.y, GetRulerHeight(false)); auto buttonMaker = [&] (wxWindowID id, teBmps bitmap, bool toggle) @@ -2113,6 +2119,8 @@ void AdornedRulerPanel::ReCreateButtons() bmpRecoloredUpSmall, bmpRecoloredDownSmall, bmpRecoloredHiliteSmall, bmpUnpinnedPlayRecordHead, bmpUnpinnedPlayRecordHead, bmpUnpinnedPlayRecordHead, size); + buttonMaker(OnScrubID, bmpScrub, true); + buttonMaker(OnSeekID, bmpSeek, true); UpdateButtonStates(); } @@ -2421,7 +2429,7 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) } else if (!HasCapture() && inScrubZone) { if (evt.LeftDown()) { - scrubber.MarkScrubStart(evt.m_x, PlaybackPrefs::GetPinnedHeadPreference(), false); + scrubber.MarkScrubStart(evt.m_x, PlaybackPrefs::GetPinnedHeadPreference()); UpdateStatusBarAndTooltips(StatusChoice::EnteringScrubZone); } ShowQuickPlayIndicator(); @@ -2730,12 +2738,10 @@ void AdornedRulerPanel::UpdateStatusBarAndTooltips(StatusChoice choice) case StatusChoice::EnteringScrubZone: { - if (scrubbing) { - if(!scrubber.IsAlwaysSeeking()) - message = _("Click or drag to seek"); - } - else - message = _("Click to scrub, Double-Click to scroll, Drag to seek"); + if(scrubber.Seeks()) + message = _("Click or drag to seek"); + else if(scrubber.Scrubs()) + message = _("Click or drag to scrub"); } break; @@ -2749,10 +2755,10 @@ void AdornedRulerPanel::UpdateStatusBarAndTooltips(StatusChoice choice) RegenerateTooltips(choice); } -void AdornedRulerPanel::OnToggleScrubbing(wxCommandEvent&) +void AdornedRulerPanel::OnToggleScrubbing(/*wxCommandEvent&*/) { mShowScrubbing = !mShowScrubbing; - WriteScrubEnabledPref(mShowScrubbing); + //WriteScrubEnabledPref(mShowScrubbing); gPrefs->Flush(); wxSize size { GetSize().GetWidth(), GetRulerHeight(mShowScrubbing) }; SetSize(size); @@ -2767,6 +2773,13 @@ void AdornedRulerPanel::OnContextMenu(wxContextMenuEvent & WXUNUSED(event)) void AdornedRulerPanel::UpdateButtonStates() { + auto common = [this] + (wxWindow *button, const wxString &commandName, const wxString &label){ + const auto &fullLabel = ComposeButtonLabel(*mProject, commandName, label); + button->SetLabel(fullLabel); + button->SetToolTip(fullLabel); + }; + { bool state = PlaybackPrefs::GetPinnedHeadPreference(); auto pinButton = static_cast(FindWindow(OnTogglePinnedStateID)); @@ -2777,10 +2790,38 @@ void AdornedRulerPanel::UpdateButtonStates() // (which is, to toggle the state) ? _("Pinned play/record Head") : _("Unpinned play/record Head"); - const auto &fullLabel = ComposeButtonLabel(*mProject, wxT("PinnedHead"), label); - pinButton->SetLabel(fullLabel); - pinButton->SetToolTip(fullLabel); + common(pinButton, wxT("PinnedHead"), label); } + + const auto scrubber = &mProject->GetScrubber(); + + { + const auto scrubButton = static_cast(FindWindow(OnScrubID)); + /* i18n-hint: These commands assist the user in finding a sound by ear. ... + "Scrubbing" is variable-speed playback + */ + common(scrubButton, wxT("Scrub"), _("Scrub")); + if (scrubber && scrubber->Scrubs()) + scrubButton->PushDown(); + else + scrubButton->PopUp(); + } + + { + const auto seekButton = static_cast(FindWindow(OnSeekID)); + /* i18n-hint: These commands assist the user in finding a sound by ear. ... + "Seeking" is normal speed playback but with skips + */ + common(seekButton, wxT("Seek"), _("Seek")); + if (scrubber && scrubber->Seeks()) + seekButton->PushDown(); + else + seekButton->PopUp(); + } + + if(scrubber && + mShowScrubbing != (scrubber->Scrubs() || scrubber->Seeks())) + OnToggleScrubbing(); } void AdornedRulerPanel::OnTogglePinnedState(wxCommandEvent & event) @@ -2789,6 +2830,16 @@ void AdornedRulerPanel::OnTogglePinnedState(wxCommandEvent & event) UpdateButtonStates(); } +void AdornedRulerPanel::OnSeek(wxCommandEvent & event) +{ + mProject->GetScrubber().OnSeek(event); +} + +void AdornedRulerPanel::OnScrub(wxCommandEvent & event) +{ + mProject->GetScrubber().OnScrub(event); +} + void AdornedRulerPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(evt)) { HideQuickPlayIndicator(); @@ -2858,12 +2909,6 @@ void AdornedRulerPanel::ShowScrubMenu(const wxPoint & pos) auto cleanup = finally([this]{ PopEventHandler(); }); wxMenu rulerMenu; - rulerMenu.AppendCheckItem(OnShowHideScrubbingID, _("Scrub Bar")); - if(mShowScrubbing) - rulerMenu.FindItem(OnShowHideScrubbingID)->Check(); - - rulerMenu.AppendSeparator(); - mProject->GetScrubber().PopulateMenu(rulerMenu); PopupMenu(&rulerMenu, pos); } @@ -3104,11 +3149,6 @@ void AdornedRulerPanel::DoDrawSelection(wxDC * dc) dc->DrawRectangle( r ); } -int AdornedRulerPanel::GetRulerHeight() -{ - return GetRulerHeight(ReadScrubEnabledPref()); -} - int AdornedRulerPanel::GetRulerHeight(bool showScrubBar) { return ProperRulerHeight + (showScrubBar ? ScrubHeight : 0); diff --git a/src/widgets/Ruler.h b/src/widgets/Ruler.h index cc5910ad1..e7f5c179e 100644 --- a/src/widgets/Ruler.h +++ b/src/widgets/Ruler.h @@ -298,7 +298,7 @@ public: #endif public: - static int GetRulerHeight(); + int GetRulerHeight() { return GetRulerHeight(mShowScrubbing); } static int GetRulerHeight(bool showScrubBar); wxRect GetInnerRect() const { return mInner; } @@ -413,7 +413,9 @@ private: void OnAutoScroll(wxCommandEvent &evt); void OnLockPlayRegion(wxCommandEvent &evt); - void OnToggleScrubbing(wxCommandEvent&); + void OnToggleScrubbing(/*wxCommandEvent&*/); + void OnScrub(wxCommandEvent&); + void OnSeek(wxCommandEvent&); void OnContextMenu(wxContextMenuEvent & WXUNUSED(event)); @@ -446,7 +448,7 @@ private: friend QuickPlayRulerOverlay; - wxWindow *mButtons[1]; + wxWindow *mButtons[3]; bool mNeedButtonUpdate { true }; };