1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-03 09:09:47 +02:00

Merge branch 'master' into scrubbing

This commit is contained in:
Paul Licameli 2016-06-03 08:34:16 -04:00
commit 7e2380d496
21 changed files with 4873 additions and 5170 deletions

View File

@ -69,6 +69,8 @@ from there. Audacity will look for a file called "Pause.png".
DEFINE_IMAGE( bmpAppendRecordDisabled, wxImage( 16, 16 ), wxT("AppendRecordDisabled")); DEFINE_IMAGE( bmpAppendRecordDisabled, wxImage( 16, 16 ), wxT("AppendRecordDisabled"));
DEFINE_IMAGE( bmpScrubDisabled, wxImage( 16, 16 ), wxT("ScrubDisabled")); DEFINE_IMAGE( bmpScrubDisabled, wxImage( 16, 16 ), wxT("ScrubDisabled"));
DEFINE_IMAGE( bmpScrub, wxImage( 16, 16 ), wxT("Scrub")); DEFINE_IMAGE( bmpScrub, wxImage( 16, 16 ), wxT("Scrub"));
DEFINE_IMAGE( bmpSeekDisabled, wxImage( 24, 16 ), wxT("SeekDisabled"));
DEFINE_IMAGE( bmpSeek, wxImage( 24, 16 ), wxT("Seek"));
SET_THEME_FLAGS( resFlagNewLine ); SET_THEME_FLAGS( resFlagNewLine );
DEFINE_IMAGE( bmpUpButtonLarge, wxImage( 48, 48 ), wxT("UpButtonLarge")); DEFINE_IMAGE( bmpUpButtonLarge, wxImage( 48, 48 ), wxT("UpButtonLarge"));
@ -123,6 +125,9 @@ from there. Audacity will look for a file called "Pause.png".
DEFINE_IMAGE( bmpMic, wxImage( 25, 25 ), wxT("Mic")); DEFINE_IMAGE( bmpMic, wxImage( 25, 25 ), wxT("Mic"));
DEFINE_IMAGE( bmpSpeaker, wxImage( 25, 25 ), wxT("Speaker")); DEFINE_IMAGE( bmpSpeaker, wxImage( 25, 25 ), wxT("Speaker"));
DEFINE_IMAGE( bmpPinnedPlayRecordHead, wxImage( 27, 27 ), wxT("PinnedPlayRecordHead"));
DEFINE_IMAGE( bmpUnpinnedPlayRecordHead, wxImage( 27, 27 ), wxT("UnpinnedPlayRecordHead"));
SET_THEME_FLAGS( resFlagPaired ); SET_THEME_FLAGS( resFlagPaired );
DEFINE_IMAGE( bmpZoomFit, wxImage( 27, 27 ), wxT("ZoomFit")); DEFINE_IMAGE( bmpZoomFit, wxImage( 27, 27 ), wxT("ZoomFit"));
DEFINE_IMAGE( bmpZoomFitDisabled, wxImage( 27, 27 ), wxT("ZoomFitDisabled")); DEFINE_IMAGE( bmpZoomFitDisabled, wxImage( 27, 27 ), wxT("ZoomFitDisabled"));

View File

@ -69,6 +69,7 @@ simplifies construction of menu items.
#include "export/Export.h" #include "export/Export.h"
#include "export/ExportMultiple.h" #include "export/ExportMultiple.h"
#include "prefs/PrefsDialog.h" #include "prefs/PrefsDialog.h"
#include "prefs/PlaybackPrefs.h"
#include "ShuttleGui.h" #include "ShuttleGui.h"
#include "HistoryWindow.h" #include "HistoryWindow.h"
#include "LyricsWindow.h" #include "LyricsWindow.h"
@ -772,6 +773,11 @@ void AudacityProject::CreateMenusAndCommands()
c->AddSeparator(); c->AddSeparator();
c->AddCheck(wxT("PinnedHead"), _("Pinned Recording/Playback Head"),
FN(OnTogglePinnedHead), 0,
// Switching of scrolling on and off is permitted even during transport
AlwaysEnabledFlag, AlwaysEnabledFlag);
c->AddCheck(wxT("Duplex"), _("&Overdub (on/off)"), FN(OnTogglePlayRecording), 0); c->AddCheck(wxT("Duplex"), _("&Overdub (on/off)"), FN(OnTogglePlayRecording), 0);
c->AddCheck(wxT("SWPlaythrough"), _("So&ftware Playthrough (on/off)"), FN(OnToggleSWPlaythrough), 0); c->AddCheck(wxT("SWPlaythrough"), _("So&ftware Playthrough (on/off)"), FN(OnToggleSWPlaythrough), 0);
@ -1840,6 +1846,10 @@ void AudacityProject::ModifyToolbarMenus()
gPrefs->Read(wxT("/AudioIO/AutomatedInputLevelAdjustment"),&active, false); gPrefs->Read(wxT("/AudioIO/AutomatedInputLevelAdjustment"),&active, false);
mCommandManager.Check(wxT("AutomatedInputLevelAdjustmentOnOff"), active); mCommandManager.Check(wxT("AutomatedInputLevelAdjustmentOnOff"), active);
#endif #endif
active = PlaybackPrefs::GetPinnedHeadPreference();
mCommandManager.Check(wxT("PinnedHead"), active);
gPrefs->Read(wxT("/AudioIO/Duplex"),&active, true); gPrefs->Read(wxT("/AudioIO/Duplex"),&active, true);
mCommandManager.Check(wxT("Duplex"), active); mCommandManager.Check(wxT("Duplex"), active);
gPrefs->Read(wxT("/AudioIO/SWPlaythrough"),&active, false); gPrefs->Read(wxT("/AudioIO/SWPlaythrough"),&active, false);
@ -2360,6 +2370,27 @@ void AudacityProject::OnToggleSoundActivated()
ModifyAllProjectToolbarMenus(); ModifyAllProjectToolbarMenus();
} }
void AudacityProject::OnTogglePinnedHead()
{
bool value = !PlaybackPrefs::GetPinnedHeadPreference();
PlaybackPrefs::SetPinnedHeadPreference(value, true);
ModifyAllProjectToolbarMenus();
// Change what happens in case transport is in progress right now
auto ctb = GetActiveProject()->GetControlToolBar();
if (ctb)
ctb->StartScrollingIfPreferred();
auto ruler = GetRulerPanel();
if (ruler)
// Update button image
ruler->UpdateButtonStates();
auto &scrubber = GetScrubber();
if (scrubber.HasStartedScrubbing())
scrubber.SetScrollScrubbing(value);
}
void AudacityProject::OnTogglePlayRecording() void AudacityProject::OnTogglePlayRecording()
{ {
bool Duplex; bool Duplex;

View File

@ -313,6 +313,7 @@ void OnResetToolBars();
void OnSoundActivated(); void OnSoundActivated();
void OnToggleSoundActivated(); void OnToggleSoundActivated();
void OnTogglePinnedHead();
void OnTogglePlayRecording(); void OnTogglePlayRecording();
void OnToggleSWPlaythrough(); void OnToggleSWPlaythrough();
#ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT

View File

@ -890,7 +890,7 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id,
mRuler = safenew AdornedRulerPanel( this, mRuler = safenew AdornedRulerPanel( this,
wxID_ANY, wxID_ANY,
wxDefaultPosition, wxDefaultPosition,
wxSize( -1, AdornedRulerPanel::GetRulerHeight() ), wxSize( -1, AdornedRulerPanel::GetRulerHeight(false) ),
&mViewInfo ); &mViewInfo );
// //

View File

@ -65,6 +65,7 @@ and use it for toolbar and window layouts too.
#include "Project.h" #include "Project.h"
#include "toolbars/ToolBar.h" #include "toolbars/ToolBar.h"
#include "toolbars/ToolManager.h" #include "toolbars/ToolManager.h"
#include "widgets/Ruler.h"
#include "ImageManipulation.h" #include "ImageManipulation.h"
#include "Theme.h" #include "Theme.h"
#include "Experimental.h" #include "Experimental.h"
@ -230,6 +231,7 @@ void Theme::ApplyUpdatedImages()
if( pToolBar ) if( pToolBar )
pToolBar->ReCreateButtons(); pToolBar->ReCreateButtons();
} }
p->GetRulerPanel()->ReCreateButtons();
} }
void Theme::RegisterImages() void Theme::RegisterImages()
@ -958,27 +960,41 @@ void ThemeBase::SaveComponents()
if( (mBitmapFlags[i] & resFlagInternal)==0) if( (mBitmapFlags[i] & resFlagInternal)==0)
{ {
FileName = FileNames::ThemeComponent( mBitmapNames[i] ); FileName = FileNames::ThemeComponent( mBitmapNames[i] );
if( !wxFileExists( FileName )) if( wxFileExists( FileName ))
{ {
if( !mImages[i].SaveFile( FileName, wxBITMAP_TYPE_PNG )) ++n;
{ break;
wxMessageBox(
wxString::Format(
_("Audacity could not save file:\n %s"),
FileName.c_str() ));
return;
}
n++;
} }
} }
} }
if( n==0 )
if (n > 0)
{ {
wxMessageBox( auto result =
wxString::Format( wxMessageBox(
_("All required files in:\n %s\nwere already present."), wxString::Format(
FileNames::ThemeComponentsDir().c_str() )); _("Some required files in:\n %s\nwere already present. Overwrite?"),
return; FileNames::ThemeComponentsDir().c_str()),
wxMessageBoxCaptionStr,
wxYES_NO | wxNO_DEFAULT);
if(result == wxNO)
return;
}
for(i=0;i<(int)mImages.GetCount();i++)
{
if( (mBitmapFlags[i] & resFlagInternal)==0)
{
FileName = FileNames::ThemeComponent( mBitmapNames[i] );
if( !mImages[i].SaveFile( FileName, wxBITMAP_TYPE_PNG ))
{
wxMessageBox(
wxString::Format(
_("Audacity could not save file:\n %s"),
FileName.c_str() ));
return;
}
}
} }
wxMessageBox( wxMessageBox(
wxString::Format( wxString::Format(

File diff suppressed because it is too large Load Diff

View File

@ -19,13 +19,25 @@
*//********************************************************************/ *//********************************************************************/
#include "../Audacity.h" #include "../Audacity.h"
#include "PlaybackPrefs.h"
#include <wx/defs.h> #include <wx/defs.h>
#include <wx/textctrl.h> #include <wx/textctrl.h>
#include "../ShuttleGui.h" #include "../ShuttleGui.h"
#include "../Prefs.h"
#include "PlaybackPrefs.h" namespace {
const wxChar *PinnedHeadPreferenceKey()
{
return wxT("/AudioIO/PinnedHead");
}
bool PinnedHeadPreferenceDefault()
{
return false;
}
}
PlaybackPrefs::PlaybackPrefs(wxWindow * parent) PlaybackPrefs::PlaybackPrefs(wxWindow * parent)
: PrefsPanel(parent, _("Playback")) : PrefsPanel(parent, _("Playback"))
@ -113,6 +125,11 @@ void PlaybackPrefs::PopulateOrExchange(ShuttleGui & S)
S.EndThreeColumn(); S.EndThreeColumn();
} }
S.EndStatic(); S.EndStatic();
// This affects recording too, though it is in playback preferences.
S.TieCheckBox(_("Pinned playback/recording head"),
PinnedHeadPreferenceKey(),
PinnedHeadPreferenceDefault());
} }
bool PlaybackPrefs::Apply() bool PlaybackPrefs::Apply()
@ -123,8 +140,21 @@ bool PlaybackPrefs::Apply()
return true; return true;
} }
bool PlaybackPrefs::GetPinnedHeadPreference()
{
return gPrefs->ReadBool(PinnedHeadPreferenceKey(), PinnedHeadPreferenceDefault());
}
void PlaybackPrefs::SetPinnedHeadPreference(bool value, bool flush)
{
gPrefs->Write(PinnedHeadPreferenceKey(), value);
if(flush)
gPrefs->Flush();
}
PrefsPanel *PlaybackPrefsFactory::Create(wxWindow *parent) PrefsPanel *PlaybackPrefsFactory::Create(wxWindow *parent)
{ {
wxASSERT(parent); // to justify safenew wxASSERT(parent); // to justify safenew
return safenew PlaybackPrefs(parent); return safenew PlaybackPrefs(parent);
} }

View File

@ -27,6 +27,9 @@ class PlaybackPrefs final : public PrefsPanel
virtual ~PlaybackPrefs(); virtual ~PlaybackPrefs();
bool Apply() override; bool Apply() override;
static bool GetPinnedHeadPreference();
static void SetPinnedHeadPreference(bool value, bool flush = false);
private: private:
void Populate(); void Populate();
void PopulateOrExchange(ShuttleGui & S); void PopulateOrExchange(ShuttleGui & S);

View File

@ -68,6 +68,7 @@
#include "../widgets/Meter.h" #include "../widgets/Meter.h"
#include "../tracks/ui/Scrubbing.h" #include "../tracks/ui/Scrubbing.h"
#include "../prefs/PlaybackPrefs.h"
IMPLEMENT_CLASS(ControlToolBar, ToolBar); IMPLEMENT_CLASS(ControlToolBar, ToolBar);
@ -129,7 +130,7 @@ AButton *ControlToolBar::MakeButton(teBmps eEnabledUp, teBmps eEnabledDown, teBm
bool processdownevents, bool processdownevents,
const wxChar *label) const wxChar *label)
{ {
AButton *r = ToolBar::MakeButton( AButton *r = ToolBar::MakeButton(this,
bmpRecoloredUpLarge, bmpRecoloredDownLarge, bmpRecoloredHiliteLarge, bmpRecoloredUpLarge, bmpRecoloredDownLarge, bmpRecoloredHiliteLarge,
eEnabledUp, eEnabledDown, eDisabled, eEnabledUp, eEnabledDown, eDisabled,
wxWindowID(id), wxWindowID(id),
@ -167,6 +168,8 @@ void ControlToolBar::Populate()
bmpCutPreview, bmpCutPreview, bmpCutPreviewDisabled); bmpCutPreview, bmpCutPreview, bmpCutPreviewDisabled);
MakeAlternateImages(*mPlay, 3, MakeAlternateImages(*mPlay, 3,
bmpScrub, bmpScrub, bmpScrubDisabled); bmpScrub, bmpScrub, bmpScrubDisabled);
MakeAlternateImages(*mPlay, 4,
bmpSeek, bmpSeek, bmpSeekDisabled);
mPlay->FollowModifierKeys(); mPlay->FollowModifierKeys();
mStop = MakeButton( bmpStop, bmpStop, bmpStopDisabled , mStop = MakeButton( bmpStop, bmpStop, bmpStopDisabled ,
@ -635,15 +638,16 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
NoteTrackArray(), NoteTrackArray(),
#endif #endif
tcp0, tcp1, myOptions); tcp0, tcp1, myOptions);
} else }
{ else {
// Cannot create cut preview tracks, clean up and exit // Cannot create cut preview tracks, clean up and exit
SetPlay(false); SetPlay(false);
SetStop(false); SetStop(false);
SetRecord(false); SetRecord(false);
return -1; return -1;
} }
} else { }
else {
// Lifted the following into AudacityProject::GetDefaultPlayOptions() // Lifted the following into AudacityProject::GetDefaultPlayOptions()
/* /*
if (!timetrack) { if (!timetrack) {
@ -682,6 +686,8 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
return -1; return -1;
} }
StartScrollingIfPreferred();
// Let other UI update appearance // Let other UI update appearance
if (p) if (p)
p->GetRulerPanel()->HideQuickPlayIndicator(); p->GetRulerPanel()->HideQuickPlayIndicator();
@ -745,25 +751,17 @@ void ControlToolBar::OnKeyEvent(wxKeyEvent & event)
void ControlToolBar::OnPlay(wxCommandEvent & WXUNUSED(evt)) void ControlToolBar::OnPlay(wxCommandEvent & WXUNUSED(evt))
{ {
auto doubleClicked = mPlay->IsDoubleClicked();
mPlay->ClearDoubleClicked();
auto p = GetActiveProject(); auto p = GetActiveProject();
if (doubleClicked) if (!CanStopAudioStream())
p->GetPlaybackScroller().Activate return;
(AudacityProject::PlaybackScroller::Mode::Centered);
else {
if (!CanStopAudioStream())
return;
StopPlaying(); StopPlaying();
if (p) p->TP_DisplaySelection(); if (p) p->TP_DisplaySelection();
PlayDefault(); PlayDefault();
UpdateStatusBar(p); UpdateStatusBar(p);
}
} }
void ControlToolBar::OnStop(wxCommandEvent & WXUNUSED(evt)) void ControlToolBar::OnStop(wxCommandEvent & WXUNUSED(evt))
@ -792,11 +790,11 @@ void ControlToolBar::PlayDefault()
void ControlToolBar::StopPlaying(bool stopStream /* = true*/) void ControlToolBar::StopPlaying(bool stopStream /* = true*/)
{ {
StopScrolling();
AudacityProject *project = GetActiveProject(); AudacityProject *project = GetActiveProject();
if(project) { if(project) {
project->GetPlaybackScroller().Activate
(AudacityProject::PlaybackScroller::Mode::Off);
// Let scrubbing code do some appearance change // Let scrubbing code do some appearance change
project->GetScrubber().StopScrubbing(); project->GetScrubber().StopScrubbing();
} }
@ -853,29 +851,6 @@ void ControlToolBar::Pause()
void ControlToolBar::OnRecord(wxCommandEvent &evt) void ControlToolBar::OnRecord(wxCommandEvent &evt)
{ {
auto doubleClicked = mRecord->IsDoubleClicked();
mRecord->ClearDoubleClicked();
if (doubleClicked) {
// Display a fixed recording head while scrolling the waves continuously.
// If you overdub, you may want to anticipate some context in existing tracks,
// so center the head. If not, put it rightmost to display as much wave as we can.
const auto project = GetActiveProject();
bool duplex;
gPrefs->Read(wxT("/AudioIO/Duplex"), &duplex, true);
if (duplex) {
// See if there is really anything being overdubbed
if (gAudioIO->GetNumPlaybackChannels() == 0)
// No.
duplex = false;
}
using Mode = AudacityProject::PlaybackScroller::Mode;
project->GetPlaybackScroller().Activate(duplex ? Mode::Centered : Mode::Right);
return;
}
if (gAudioIO->IsBusy()) { if (gAudioIO->IsBusy()) {
if (!CanStopAudioStream() || 0 == gAudioIO->GetNumCaptureChannels()) if (!CanStopAudioStream() || 0 == gAudioIO->GetNumCaptureChannels())
mRecord->PopUp(); mRecord->PopUp();
@ -1092,6 +1067,8 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
if (success) { if (success) {
p->SetAudioIOToken(token); p->SetAudioIOToken(token);
mBusyProject = p; mBusyProject = p;
StartScrollingIfPreferred();
} }
else { else {
if (shifted) { if (shifted) {
@ -1278,3 +1255,60 @@ void ControlToolBar::UpdateStatusBar(AudacityProject *pProject)
{ {
pProject->GetStatusBar()->SetStatusText(StateForStatusBar(), stateStatusBarField); pProject->GetStatusBar()->SetStatusText(StateForStatusBar(), stateStatusBarField);
} }
void ControlToolBar::StartScrollingIfPreferred()
{
if (PlaybackPrefs::GetPinnedHeadPreference())
StartScrolling();
#ifdef __WXMAC__
else if (::GetActiveProject()->GetScrubber().HasStartedScrubbing()) {
// PRL: cause many "unnecessary" refreshes. For reasons I don't understand,
// doing this causes wheel rotation events (mapped from the double finger vertical
// swipe) to be delivered more uniformly to the application, so that speed control
// works better.
::GetActiveProject()->GetPlaybackScroller().Activate
(AudacityProject::PlaybackScroller::Mode::Refresh);
}
#endif
else
StopScrolling();
}
void ControlToolBar::StartScrolling()
{
using Mode = AudacityProject::PlaybackScroller::Mode;
const auto project = GetActiveProject();
if (project) {
auto mode = Mode::Centered;
if (gAudioIO->GetNumCaptureChannels() > 0) {
// recording
// Display a fixed recording head while scrolling the waves continuously.
// If you overdub, you may want to anticipate some context in existing tracks,
// so center the head. If not, put it rightmost to display as much wave as we can.
bool duplex;
gPrefs->Read(wxT("/AudioIO/Duplex"), &duplex, true);
if (duplex) {
// See if there is really anything being overdubbed
if (gAudioIO->GetNumPlaybackChannels() == 0)
// No.
duplex = false;
}
if (!duplex)
mode = Mode::Right;
}
project->GetPlaybackScroller().Activate(mode);
}
}
void ControlToolBar::StopScrolling()
{
const auto project = GetActiveProject();
if(project)
project->GetPlaybackScroller().Activate
(AudacityProject::PlaybackScroller::Mode::Off);
}

View File

@ -62,7 +62,7 @@ class ControlToolBar final : public ToolBar {
// Choice among the appearances of the play button: // Choice among the appearances of the play button:
enum class PlayAppearance { enum class PlayAppearance {
Straight, Looped, CutPreview, Scrub Straight, Looped, CutPreview, Scrub, Seek
}; };
//These allow buttons to be controlled externally: //These allow buttons to be controlled externally:
@ -106,6 +106,11 @@ class ControlToolBar final : public ToolBar {
int WidthForStatusBar(wxStatusBar* const); int WidthForStatusBar(wxStatusBar* const);
void UpdateStatusBar(AudacityProject *pProject); void UpdateStatusBar(AudacityProject *pProject);
// Starting and stopping of scrolling display
void StartScrollingIfPreferred();
void StartScrolling();
void StopScrolling();
private: private:
AButton *MakeButton(teBmps eEnabledUp, teBmps eEnabledDown, teBmps eDisabled, AButton *MakeButton(teBmps eEnabledUp, teBmps eEnabledDown, teBmps eDisabled,

View File

@ -105,7 +105,7 @@ AButton *EditToolBar::AddButton(
{ {
AButton *&r = mButtons[id]; AButton *&r = mButtons[id];
r = ToolBar::MakeButton( r = ToolBar::MakeButton(this,
bmpRecoloredUpSmall, bmpRecoloredDownSmall, bmpRecoloredHiliteSmall, bmpRecoloredUpSmall, bmpRecoloredDownSmall, bmpRecoloredHiliteSmall,
eEnabledUp, eEnabledDown, eDisabled, eEnabledUp, eEnabledDown, eDisabled,
wxWindowID(id), wxWindowID(id),

View File

@ -700,6 +700,7 @@ void ToolBar::MakeButtonBackgroundsSmall()
} }
/// Makes a button and its four different state bitmaps /// Makes a button and its four different state bitmaps
/// @param parent Parent window for the button.
/// @param eUp Background for when button is Up. /// @param eUp Background for when button is Up.
/// @param eDown Background for when button is Down. /// @param eDown Background for when button is Down.
/// @param eHilite Background for when button is Hilit. /// @param eHilite Background for when button is Hilit.
@ -710,7 +711,8 @@ void ToolBar::MakeButtonBackgroundsSmall()
/// @param placement Placement position /// @param placement Placement position
/// @param processdownevents true iff button handles down events. /// @param processdownevents true iff button handles down events.
/// @param size Size of the background. /// @param size Size of the background.
AButton * ToolBar::MakeButton(teBmps eUp, AButton * ToolBar::MakeButton(wxWindow *parent,
teBmps eUp,
teBmps eDown, teBmps eDown,
teBmps eHilite, teBmps eHilite,
teBmps eStandardUp, teBmps eStandardUp,
@ -731,7 +733,7 @@ AButton * ToolBar::MakeButton(teBmps eUp,
wxImagePtr disable2 (OverlayImage(eUp, eDisabled, xoff, yoff)); wxImagePtr disable2 (OverlayImage(eUp, eDisabled, xoff, yoff));
AButton * button = AButton * button =
new AButton(this, id, placement, size, *up2, *hilite2, *down2, new AButton(parent, id, placement, size, *up2, *hilite2, *down2,
*disable2, processdownevents); *disable2, processdownevents);
return button; return button;

View File

@ -121,9 +121,11 @@ class ToolBar /* not final */ : public wxPanel
virtual int GetInitialWidth() { return -1; } virtual int GetInitialWidth() { return -1; }
virtual int GetMinToolbarWidth() { return GetInitialWidth(); } virtual int GetMinToolbarWidth() { return GetInitialWidth(); }
virtual wxSize GetDockedSize() { return GetMinSize(); } virtual wxSize GetDockedSize() { return GetMinSize(); }
protected:
AButton *MakeButton(teBmps eUp, public:
static
AButton *MakeButton(wxWindow *parent,
teBmps eUp,
teBmps eDown, teBmps eDown,
teBmps eHilite, teBmps eHilite,
teBmps eStandardUp, teBmps eStandardUp,
@ -144,6 +146,7 @@ class ToolBar /* not final */ : public wxPanel
teBmps eDisabled, teBmps eDisabled,
wxSize size); wxSize size);
protected:
void SetButton(bool down, AButton *button); void SetButton(bool down, AButton *button);
void MakeMacRecoloredImage(teBmps eBmpOut, teBmps eBmpIn); void MakeMacRecoloredImage(teBmps eBmpOut, teBmps eBmpIn);

View File

@ -156,7 +156,7 @@ void ToolsToolBar::UpdatePrefs()
AButton * ToolsToolBar::MakeTool( teBmps eTool, AButton * ToolsToolBar::MakeTool( teBmps eTool,
int id, const wxChar *label) int id, const wxChar *label)
{ {
AButton *button = ToolBar::MakeButton( AButton *button = ToolBar::MakeButton(this,
bmpRecoloredUpSmall, bmpRecoloredDownSmall, bmpRecoloredHiliteSmall, bmpRecoloredUpSmall, bmpRecoloredDownSmall, bmpRecoloredHiliteSmall,
eTool, eTool, eTool, eTool, eTool, eTool,
wxWindowID(id), wxWindowID(id),

View File

@ -147,7 +147,7 @@ AButton *TranscriptionToolBar::AddButton(
{ {
AButton *&r = mButtons[id]; AButton *&r = mButtons[id];
r = ToolBar::MakeButton( r = ToolBar::MakeButton(this,
bmpRecoloredUpSmall, bmpRecoloredDownSmall, bmpRecoloredHiliteSmall, bmpRecoloredUpSmall, bmpRecoloredDownSmall, bmpRecoloredHiliteSmall,
eFore, eFore, eDisabled, eFore, eFore, eDisabled,
wxWindowID(id), wxWindowID(id),
@ -476,23 +476,11 @@ void TranscriptionToolBar::OnPlaySpeed(wxCommandEvent & WXUNUSED(event))
{ {
auto button = mButtons[TTB_PlaySpeed]; auto button = mButtons[TTB_PlaySpeed];
auto doubleClicked = button->IsDoubleClicked(); // Let control have precedence over shift
button->ClearDoubleClicked(); const bool cutPreview = mButtons[TTB_PlaySpeed]->WasControlDown();
const bool looped = !cutPreview &&
if (doubleClicked) { button->WasShiftDown();
GetActiveProject()->GetPlaybackScroller().Activate PlayAtSpeed(looped, cutPreview);
(AudacityProject::PlaybackScroller::Mode::Centered);
// Pop up the button
SetButton(false, button);
}
else {
// Let control have precedence over shift
const bool cutPreview = mButtons[TTB_PlaySpeed]->WasControlDown();
const bool looped = !cutPreview &&
button->WasShiftDown();
PlayAtSpeed(looped, cutPreview);
}
} }
void TranscriptionToolBar::OnSpeedSlider(wxCommandEvent& WXUNUSED(event)) void TranscriptionToolBar::OnSpeedSlider(wxCommandEvent& WXUNUSED(event))

View File

@ -99,7 +99,7 @@ void PlayIndicatorOverlayBase::Draw(OverlayPanel &panel, wxDC &dc)
else if(auto ruler = dynamic_cast<AdornedRulerPanel*>(&panel)) { else if(auto ruler = dynamic_cast<AdornedRulerPanel*>(&panel)) {
wxASSERT(!mIsMaster); wxASSERT(!mIsMaster);
ruler->DoDrawIndicator(&dc, mLastIndicatorX, !rec, IndicatorMediumWidth, false); ruler->DoDrawIndicator(&dc, mLastIndicatorX, !rec, IndicatorMediumWidth, false, false);
} }
else else
wxASSERT(false); wxASSERT(false);

View File

@ -19,6 +19,7 @@ Paul Licameli split from TrackPanel.cpp
#include "../../TrackPanelCell.h" #include "../../TrackPanelCell.h"
#include "../../TrackPanelCellIterator.h" #include "../../TrackPanelCellIterator.h"
#include "../../commands/CommandFunctors.h" #include "../../commands/CommandFunctors.h"
#include "../../prefs/PlaybackPrefs.h"
#include "../../toolbars/ControlToolBar.h" #include "../../toolbars/ControlToolBar.h"
#undef USE_TRANSCRIPTION_TOOLBAR #undef USE_TRANSCRIPTION_TOOLBAR
@ -32,9 +33,6 @@ Paul Licameli split from TrackPanel.cpp
#include <wx/dc.h> #include <wx/dc.h>
// Conditional compilation switch for making scrub menu items checkable
#define CHECKABLE_SCRUB_MENU_ITEMS
enum { enum {
// PRL: // PRL:
// Mouse must move at least this far to distinguish ctrl-drag to scrub // Mouse must move at least this far to distinguish ctrl-drag to scrub
@ -219,37 +217,33 @@ namespace {
wxString label; wxString label;
wxString status; wxString status;
void (Scrubber::*memFn)(wxCommandEvent&); void (Scrubber::*memFn)(wxCommandEvent&);
bool scroll;
bool seek; bool seek;
bool (Scrubber::*StatusTest)() const;
const wxString &GetStatus() const { return status; } const wxString &GetStatus() const { return status; }
} menuItems[] = { } menuItems[] = {
/* i18n-hint: These commands assist the user in finding a sound by ear. ... /* i18n-hint: These commands assist the user in finding a sound by ear. ...
"Scrubbing" is variable-speed playback, ... "Scrubbing" is variable-speed playback, ...
"Seeking" is normal speed playback but with skips, ... "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"), { wxT("Scrub"), XO("&Scrub"), XO("Scrubbing"),
&Scrubber::OnScrub, false, false }, &Scrubber::OnScrub, false, &Scrubber::Scrubs },
{ wxT("ScrollScrub"), XO("Sc&rolling Scrub"), XO("Scrolling Scrub"),
&Scrubber::OnScrollScrub, true, false },
{ wxT("Seek"), XO("See&k"), XO("Seeking"), { wxT("Seek"), XO("See&k"), XO("Seeking"),
&Scrubber::OnSeek, false, true }, &Scrubber::OnSeek, true, &Scrubber::Seeks },
{ wxT("ScrollSeek"), XO("Scro&lling Seek"), XO("Scrolling Seek"), { wxT("StartScrubSeek"), XO("Star&t"), XO(""),
&Scrubber::OnScrollSeek, true, true } &Scrubber::OnStart, true, nullptr },
}; };
enum { nMenuItems = sizeof(menuItems) / sizeof(*menuItems) }; enum { nMenuItems = sizeof(menuItems) / sizeof(*menuItems), StartMenuItem = 2 };
inline const MenuItem &FindMenuItem(bool scroll, bool seek) // This never finds the last item:
inline const MenuItem &FindMenuItem(bool seek)
{ {
return *std::find_if(menuItems, menuItems + nMenuItems, return *std::find_if(menuItems, menuItems + nMenuItems,
[=](const MenuItem &item) { [=](const MenuItem &item) {
return scroll == item.scroll && return seek == item.seek;
seek == item.seek;
} }
); );
} }
@ -258,16 +252,13 @@ namespace {
void Scrubber::MarkScrubStart( void Scrubber::MarkScrubStart(
// Assume xx is relative to the left edge of TrackPanel! // 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 // Don't actually start scrubbing, but collect some information
// needed for the decision to start scrubbing later when handling // needed for the decision to start scrubbing later when handling
// drag events. // drag events.
mSmoothScrollingScrub = smoothScrolling; mSmoothScrollingScrub = smoothScrolling;
mAlwaysSeeking = alwaysSeeking;
ControlToolBar * const ctb = mProject->GetControlToolBar(); ControlToolBar * const ctb = mProject->GetControlToolBar();
@ -278,14 +269,14 @@ void Scrubber::MarkScrubStart(
// scrubber state // scrubber state
mProject->SetAudioIOToken(0); mProject->SetAudioIOToken(0);
ctb->SetPlay(true, ControlToolBar::PlayAppearance::Scrub); ctb->SetPlay(true, mSeeking
? ControlToolBar::PlayAppearance::Seek
: ControlToolBar::PlayAppearance::Scrub);
ctb->UpdateStatusBar(mProject); ctb->UpdateStatusBar(mProject);
mScrubStartPosition = xx; mScrubStartPosition = xx;
mOptions.startClockTimeMillis = ::wxGetLocalTimeMillis(); mOptions.startClockTimeMillis = ::wxGetLocalTimeMillis();
CheckMenuItem();
} }
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
@ -358,10 +349,11 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx)
mOptions.maxSample = mOptions.maxSample =
lrint(std::max(0.0, mProject->GetTracks()->GetEndTime()) * options.rate); lrint(std::max(0.0, mProject->GetTracks()->GetEndTime()) * options.rate);
mOptions.minStutter = mOptions.minStutter =
lrint(std::max(0.0, MinStutter) * options.rate); mDragging ? 0.0 : lrint(std::max(0.0, MinStutter) * options.rate);
ControlToolBar::PlayAppearance appearance = ControlToolBar::PlayAppearance appearance = mSeeking
ControlToolBar::PlayAppearance::Scrub; ? ControlToolBar::PlayAppearance::Seek
: ControlToolBar::PlayAppearance::Scrub;
const bool cutPreview = false; const bool cutPreview = false;
const bool backwards = time1 < time0; const bool backwards = time1 < time0;
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL #ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
@ -382,7 +374,6 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx)
mOptions.startClockTimeMillis = ::wxGetLocalTimeMillis(); mOptions.startClockTimeMillis = ::wxGetLocalTimeMillis();
if (IsScrubbing()) { if (IsScrubbing()) {
ActivateScroller();
mPaused = false; mPaused = false;
mLastScrubPosition = xx; mLastScrubPosition = xx;
@ -475,12 +466,12 @@ void Scrubber::ContinueScrubbingUI()
{ {
// Show the correct status for seeking. // Show the correct status for seeking.
bool backup = mAlwaysSeeking; bool backup = mSeeking;
mAlwaysSeeking = seek; mSeeking = seek;
const auto ctb = mProject->GetControlToolBar(); const auto ctb = mProject->GetControlToolBar();
if (ctb) if (ctb)
ctb->UpdateStatusBar(mProject); ctb->UpdateStatusBar(mProject);
mAlwaysSeeking = backup; mSeeking = backup;
} }
if (seek) if (seek)
@ -505,11 +496,7 @@ void Scrubber::StopScrubbing()
mPoller->Stop(); mPoller->Stop();
UncheckAllMenuItems();
mScrubStartPosition = -1; mScrubStartPosition = -1;
mProject->GetPlaybackScroller().Activate
(AudacityProject::PlaybackScroller::Mode::Off);
mDragging = false; mDragging = false;
if (!IsScrubbing()) if (!IsScrubbing())
@ -779,31 +766,13 @@ Scrubber &ScrubbingOverlay::GetScrubber()
bool Scrubber::PollIsSeeking() bool Scrubber::PollIsSeeking()
{ {
return mDragging || (mAlwaysSeeking || ::wxGetMouseState().LeftIsDown()); return mDragging || (mSeeking || ::wxGetMouseState().LeftIsDown());
} }
void Scrubber::ActivateScroller() void Scrubber::DoScrub()
{
using Mode = AudacityProject::PlaybackScroller::Mode;
mProject->GetPlaybackScroller().Activate(mSmoothScrollingScrub
? Mode::Centered
:
#ifdef __WXMAC__
// PRL: cause many "unnecessary" refreshes. For reasons I don't understand,
// doing this causes wheel rotation events (mapped from the double finger vertical
// swipe) to be delivered more uniformly to the application, so that spped control
// works better.
Mode::Refresh
#else
Mode::Off
#endif
);
}
void Scrubber::DoScrub(bool scroll, bool seek)
{ {
const bool wasScrubbing = IsScrubbing(); const bool wasScrubbing = IsScrubbing();
const bool match = (scroll == mSmoothScrollingScrub && seek == mAlwaysSeeking); const bool scroll = PlaybackPrefs::GetPinnedHeadPreference();
if (!wasScrubbing) { if (!wasScrubbing) {
auto tp = mProject->GetTrackPanel(); auto tp = mProject->GetTrackPanel();
wxCoord xx = tp->ScreenToClient(::wxGetMouseState().GetPosition()).x; wxCoord xx = tp->ScreenToClient(::wxGetMouseState().GetPosition()).x;
@ -814,67 +783,65 @@ void Scrubber::DoScrub(bool scroll, bool seek)
const auto offset = tp->GetLeftOffset(); const auto offset = tp->GetLeftOffset();
xx = (std::max(offset, std::min(offset + width - 1, xx))); xx = (std::max(offset, std::min(offset + width - 1, xx)));
MarkScrubStart(xx, scroll, seek); MarkScrubStart(xx, scroll);
} }
else if(!match) { }
mSmoothScrollingScrub = scroll;
ActivateScroller();
mAlwaysSeeking = seek;
UncheckAllMenuItems();
CheckMenuItem();
void Scrubber::OnScrubOrSeek(bool &toToggle, bool &other)
{
toToggle = !toToggle;
if (toToggle)
other = false;
if (HasStartedScrubbing()) {
// Show the correct status. // Show the correct status.
const auto ctb = mProject->GetControlToolBar(); const auto ctb = mProject->GetControlToolBar();
ctb->UpdateStatusBar(mProject); ctb->UpdateStatusBar(mProject);
} }
else {
// This will call back to Scrubber::StopScrubbing auto ruler = mProject->GetRulerPanel();
const auto ctb = mProject->GetControlToolBar(); if (ruler)
ctb->StopPlaying(); // Update button images
} ruler->UpdateButtonStates();
CheckMenuItem();
} }
void Scrubber::OnScrub(wxCommandEvent&) void Scrubber::OnScrub(wxCommandEvent&)
{ {
DoScrub(false, false); OnScrubOrSeek(mScrubbing, mSeeking);
}
void Scrubber::OnScrollScrub(wxCommandEvent&)
{
DoScrub(true, false);
} }
void Scrubber::OnSeek(wxCommandEvent&) void Scrubber::OnSeek(wxCommandEvent&)
{ {
DoScrub(false, true); OnScrubOrSeek(mSeeking, mScrubbing);
} }
void Scrubber::OnScrollSeek(wxCommandEvent&) void Scrubber::OnStart(wxCommandEvent&)
{ {
DoScrub(true, true); DoScrub();
} }
enum { CMD_ID = 8000 }; enum { CMD_ID = 8000 };
BEGIN_EVENT_TABLE(Scrubber, wxEvtHandler) BEGIN_EVENT_TABLE(Scrubber, wxEvtHandler)
EVT_MENU(CMD_ID, Scrubber::OnScrub) EVT_MENU(CMD_ID, Scrubber::OnScrub)
EVT_MENU(CMD_ID + 1, Scrubber::OnScrollScrub) EVT_MENU(CMD_ID + 1, Scrubber::OnSeek)
EVT_MENU(CMD_ID + 2, Scrubber::OnSeek) EVT_MENU(CMD_ID + 2, Scrubber::OnStart)
EVT_MENU(CMD_ID + 3, Scrubber::OnScrollSeek)
END_EVENT_TABLE() END_EVENT_TABLE()
BEGIN_EVENT_TABLE(Scrubber::Forwarder, wxEvtHandler) BEGIN_EVENT_TABLE(Scrubber::Forwarder, wxEvtHandler)
EVT_MOUSE_EVENTS(Scrubber::Forwarder::OnMouse) EVT_MOUSE_EVENTS(Scrubber::Forwarder::OnMouse)
END_EVENT_TABLE() END_EVENT_TABLE()
static_assert(nMenuItems == 4, "wrong number of items"); static_assert(nMenuItems == 3, "wrong number of items");
const wxString &Scrubber::GetUntranslatedStateString() const const wxString &Scrubber::GetUntranslatedStateString() const
{ {
static wxString empty; static wxString empty;
if (HasStartedScrubbing()) { if (HasStartedScrubbing()) {
auto &item = FindMenuItem(mSmoothScrollingScrub, mAlwaysSeeking); auto &item = FindMenuItem(mSeeking);
return item.status; return item.status;
} }
else else
@ -885,15 +852,19 @@ std::vector<wxString> Scrubber::GetAllUntranslatedStatusStrings()
{ {
using namespace std; using namespace std;
vector<wxString> results; vector<wxString> results;
transform(menuItems, menuItems + nMenuItems, back_inserter(results), for (const auto &item : menuItems) {
mem_fun_ref(&MenuItem::GetStatus)); const auto &status = item.GetStatus();
if (!status.empty())
results.push_back(status);
}
return move(results); return move(results);
} }
bool Scrubber::CanScrub() const bool Scrubber::CanScrub() const
{ {
// Return the enabled state for the menu item that really launches the scrub or seek.
auto cm = mProject->GetCommandManager(); auto cm = mProject->GetCommandManager();
return cm->GetEnabled(menuItems[0].name); return cm->GetEnabled(menuItems[StartMenuItem].name);
} }
void Scrubber::AddMenuItems() void Scrubber::AddMenuItems()
@ -904,15 +875,14 @@ void Scrubber::AddMenuItems()
cm->BeginSubMenu(_("Scru&bbing")); cm->BeginSubMenu(_("Scru&bbing"));
for (const auto &item : menuItems) { for (const auto &item : menuItems) {
#ifdef CHECKABLE_SCRUB_MENU_ITEMS if (!item.GetStatus().empty())
cm->AddCheck(item.name, wxGetTranslation(item.label), cm->AddCheck(item.name, wxGetTranslation(item.label),
FNT(Scrubber, this, item.memFn), FNT(Scrubber, this, item.memFn),
false, flags, mask); false, flags, mask);
#else else
cm->AddItem(item.name, wxGetTranslation(item.label), cm->AddItem(item.name, wxGetTranslation(item.label),
FNT(Scrubber, this, item.memFn), FNT(Scrubber, this, item.memFn),
flags, mask); flags, mask);
#endif
} }
cm->EndSubMenu(); cm->EndSubMenu();
CheckMenuItem(); CheckMenuItem();
@ -922,42 +892,22 @@ void Scrubber::PopulateMenu(wxMenu &menu)
{ {
int id = CMD_ID; int id = CMD_ID;
auto cm = mProject->GetCommandManager(); auto cm = mProject->GetCommandManager();
const MenuItem *checkedItem = const MenuItem *checkedItem = &FindMenuItem(mSeeking);
HasStartedScrubbing()
? &FindMenuItem(mSmoothScrollingScrub, mAlwaysSeeking)
: nullptr;
for (const auto &item : menuItems) { for (const auto &item : menuItems) {
if (cm->GetEnabled(item.name)) { if (cm->GetEnabled(item.name)) {
#ifdef CHECKABLE_SCRUB_MENU_ITEMS
menu.AppendCheckItem(id, item.label); menu.AppendCheckItem(id, item.label);
if(&item == checkedItem) if(&item == checkedItem)
menu.FindItem(id)->Check(); menu.FindItem(id)->Check();
#else
menu.Append(id, item.label);
#endif
} }
++id; ++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() void Scrubber::CheckMenuItem()
{ {
#ifdef CHECKABLE_SCRUB_MENU_ITEMS auto cm = mProject->GetCommandManager();
if(HasStartedScrubbing()) { cm->Check(menuItems[0].name, mScrubbing);
auto cm = mProject->GetCommandManager(); cm->Check(menuItems[1].name, mSeeking);
auto item = FindMenuItem(mSmoothScrollingScrub, mAlwaysSeeking);
cm->Check(item.name, true);
}
#endif
} }
#endif #endif

View File

@ -24,7 +24,11 @@ class AudacityProject;
// Conditionally compile either a separate thead, or else use a timer in the main // Conditionally compile either a separate thead, or else use a timer in the main
// thread, to poll the mouse and update scrubbing speed and direction. The advantage of // thread, to poll the mouse and update scrubbing speed and direction. The advantage of
// a thread may be immunity to choppy scrubbing in case redrawing takes too much time. // a thread may be immunity to choppy scrubbing in case redrawing takes too much time.
#ifdef __WXGTK__
// Unfortunately some things the thread needs to do are not thread safe
#else
#define USE_SCRUB_THREAD #define USE_SCRUB_THREAD
#endif
// For putting an increment of work in the scrubbing queue // For putting an increment of work in the scrubbing queue
struct ScrubbingOptions { struct ScrubbingOptions {
@ -67,11 +71,7 @@ public:
~Scrubber(); ~Scrubber();
// Assume xx is relative to the left edge of TrackPanel! // Assume xx is relative to the left edge of TrackPanel!
void MarkScrubStart( void MarkScrubStart(wxCoord xx, bool smoothScrolling);
wxCoord xx, bool smoothScrolling,
bool alwaysSeeking // if false, can switch seeking or scrubbing
// by mouse button state
);
// Returns true iff the event should be considered consumed by this: // Returns true iff the event should be considered consumed by this:
// Assume xx is relative to the left edge of TrackPanel! // Assume xx is relative to the left edge of TrackPanel!
@ -94,9 +94,14 @@ public:
bool IsScrollScrubbing() const // If true, implies HasStartedScrubbing() bool IsScrollScrubbing() const // If true, implies HasStartedScrubbing()
{ return mSmoothScrollingScrub; } { return mSmoothScrollingScrub; }
void SetScrollScrubbing(bool value)
{ mSmoothScrollingScrub = value; }
bool IsAlwaysSeeking() const bool Seeks() const
{ return mAlwaysSeeking; } { return mSeeking; }
bool Scrubs() const
{ return mScrubbing; }
bool ShouldDrawScrubSpeed(); bool ShouldDrawScrubSpeed();
double FindScrubSpeed(bool seeking, double time) const; double FindScrubSpeed(bool seeking, double time) const;
@ -114,12 +119,13 @@ public:
// For popup // For popup
void PopulateMenu(wxMenu &menu); void PopulateMenu(wxMenu &menu);
void OnScrubOrSeek(bool &toToggle, bool &other);
void OnScrub(wxCommandEvent&); void OnScrub(wxCommandEvent&);
void OnScrollScrub(wxCommandEvent&);
void OnSeek(wxCommandEvent&); void OnSeek(wxCommandEvent&);
void OnScrollSeek(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; const wxString &GetUntranslatedStateString() const;
// All possible status strings. // All possible status strings.
@ -129,10 +135,8 @@ public:
bool IsPaused() const; bool IsPaused() const;
private: private:
void ActivateScroller(); void DoScrub();
void DoScrub(bool scroll, bool seek);
void OnActivateOrDeactivateApp(wxActivateEvent & event); void OnActivateOrDeactivateApp(wxActivateEvent & event);
void UncheckAllMenuItems();
void CheckMenuItem(); void CheckMenuItem();
// I need this because I can't push the scrubber as an event handler // I need this because I can't push the scrubber as an event handler
@ -155,7 +159,12 @@ private:
wxCoord mLastScrubPosition {}; wxCoord mLastScrubPosition {};
bool mScrubSeekPress; bool mScrubSeekPress;
bool mSmoothScrollingScrub; 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 {}; bool mDragging {};
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL #ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL

View File

@ -392,18 +392,6 @@ void AButton::OnMouseEvent(wxMouseEvent & event)
(event.m_x >= 0 && event.m_y >= 0 && (event.m_x >= 0 && event.m_y >= 0 &&
event.m_x < clientSize.x && event.m_y < clientSize.y); event.m_x < clientSize.x && event.m_y < clientSize.y);
if (!mButtonIsDown)
{
// Note that CMD (or CTRL) takes precedence over Shift if both are down
// see also AButton::Listener::OnKeyUp()
if (event.CmdDown() && HasAlternateImages(2))
mAlternateIdx = 2;
else if (event.ShiftDown() && HasAlternateImages(1))
mAlternateIdx = 1;
else
mAlternateIdx = 0;
}
if (mEnabled && event.IsButton()) { if (mEnabled && event.IsButton()) {
if (event.ButtonIsDown(wxMOUSE_BTN_ANY)) { if (event.ButtonIsDown(wxMOUSE_BTN_ANY)) {
mIsClicking = true; mIsClicking = true;

File diff suppressed because it is too large Load Diff

View File

@ -298,7 +298,7 @@ public:
#endif #endif
public: public:
static int GetRulerHeight(); int GetRulerHeight() { return GetRulerHeight(mShowScrubbing); }
static int GetRulerHeight(bool showScrubBar); static int GetRulerHeight(bool showScrubBar);
wxRect GetInnerRect() const { return mInner; } wxRect GetInnerRect() const { return mInner; }
@ -315,39 +315,14 @@ public:
void InvalidateRuler(); void InvalidateRuler();
void UpdatePrefs(); void UpdatePrefs();
void ReCreateButtons();
enum class StatusChoice { enum class StatusChoice {
FirstButton = 0, EnteringQP,
QuickPlayButton = FirstButton,
ScrubBarButton,
NumButtons,
LastButton = NumButtons - 1,
NoButton = -1,
EnteringQP = NumButtons,
EnteringScrubZone, EnteringScrubZone,
Leaving, Leaving,
NoChange NoChange
}; };
enum class PointerState {
Out = 0, In, InArrow
};
struct CaptureState {
CaptureState() {}
CaptureState(StatusChoice s, PointerState p) : button(s), state(p) {}
StatusChoice button { StatusChoice::NoButton };
PointerState state { PointerState::Out };
};
friend inline StatusChoice &operator++ (StatusChoice &choice) {
choice = static_cast<StatusChoice>(1 + static_cast<int>(choice));
return choice;
}
friend inline StatusChoice &operator-- (StatusChoice &choice) {
choice = static_cast<StatusChoice>(-1 + static_cast<int>(choice));
return choice;
}
void RegenerateTooltips(StatusChoice choice); void RegenerateTooltips(StatusChoice choice);
@ -361,21 +336,11 @@ private:
void OnSize(wxSizeEvent &evt); void OnSize(wxSizeEvent &evt);
void UpdateRects(); void UpdateRects();
void OnMouseEvents(wxMouseEvent &evt); void OnMouseEvents(wxMouseEvent &evt);
void HandleQPDoubleClick(wxMouseEvent &event, wxCoord mousePosX);
void HandleQPClick(wxMouseEvent &event, wxCoord mousePosX); void HandleQPClick(wxMouseEvent &event, wxCoord mousePosX);
void HandleQPDrag(wxMouseEvent &event, wxCoord mousePosX); void HandleQPDrag(wxMouseEvent &event, wxCoord mousePosX);
void HandleQPRelease(wxMouseEvent &event); void HandleQPRelease(wxMouseEvent &event);
void StartQPPlay(bool looped, bool cutPreview); void StartQPPlay(bool looped, bool cutPreview);
static inline bool IsButton(StatusChoice choice)
{
auto integer = static_cast<int>(choice);
return integer >= 0 &&
integer < static_cast<int>(StatusChoice::NumButtons);
}
static inline bool IsButton(int choice)
{ return IsButton(static_cast<StatusChoice>(choice)); }
void UpdateStatusBarAndTooltips(StatusChoice choice); void UpdateStatusBarAndTooltips(StatusChoice choice);
void OnCaptureLost(wxMouseCaptureLostEvent &evt); void OnCaptureLost(wxMouseCaptureLostEvent &evt);
@ -384,40 +349,18 @@ private:
void DoDrawEdge(wxDC *dc); void DoDrawEdge(wxDC *dc);
void DoDrawMarks(wxDC * dc, bool /*text */ ); void DoDrawMarks(wxDC * dc, bool /*text */ );
void DoDrawSelection(wxDC * dc); void DoDrawSelection(wxDC * dc);
public: public:
void DoDrawIndicator(wxDC * dc, wxCoord xx, bool playing, int width, bool scrub); void DoDrawIndicator(wxDC * dc, wxCoord xx, bool playing, int width, bool scrub, bool seek);
void UpdateButtonStates();
private: private:
QuickPlayIndicatorOverlay *GetOverlay(); QuickPlayIndicatorOverlay *GetOverlay();
void ShowOrHideQuickPlayIndicator(bool show); void ShowOrHideQuickPlayIndicator(bool show);
void DoDrawPlayRegion(wxDC * dc); void DoDrawPlayRegion(wxDC * dc);
wxRect GetButtonAreaRect(bool includeBorder = false) const; enum class MenuChoice { QuickPlay, Scrub };
void ShowContextMenu( MenuChoice choice, const wxPoint *pPosition);
struct ButtonStrings {
wxString label, enable, disable;
};
static const ButtonStrings PushbuttonLabels[];
static const ButtonStrings *GetPushButtonStrings(StatusChoice choice)
{
if(IsButton(choice))
return &PushbuttonLabels[static_cast<size_t>(choice)];
return nullptr;
}
wxRect GetButtonRect( StatusChoice button ) const;
PointerState InButtonRect( StatusChoice button, wxMouseEvent *pEvent ) const;
CaptureState FindButton( wxMouseEvent &mouseEvent ) const;
bool GetButtonState( StatusChoice button ) const;
void ToggleButtonState( StatusChoice button );
void ShowButtonMenu( StatusChoice button, const wxPoint *pPosition);
void DoDrawPushbutton
(wxDC *dc, StatusChoice button, bool buttonState, bool arrowState) const;
void DoDrawPushbuttons(wxDC *dc) const;
void HandlePushbuttonClick(wxMouseEvent &evt);
void HandlePushbuttonEvent(wxMouseEvent &evt);
wxFont &GetButtonFont() const;
double Pos2Time(int p, bool ignoreFisheye = false); double Pos2Time(int p, bool ignoreFisheye = false);
int Time2Pos(double t, bool ignoreFisheye = false); int Time2Pos(double t, bool ignoreFisheye = false);
@ -470,20 +413,18 @@ private:
void OnAutoScroll(wxCommandEvent &evt); void OnAutoScroll(wxCommandEvent &evt);
void OnLockPlayRegion(wxCommandEvent &evt); void OnLockPlayRegion(wxCommandEvent &evt);
void OnToggleScrubbing(wxCommandEvent&); void OnToggleScrubbing(/*wxCommandEvent&*/);
void OnScrub(wxCommandEvent&);
void OnSeek(wxCommandEvent&);
void OnCaptureKey(wxCommandEvent &event);
void OnKeyDown(wxKeyEvent &event);
void OnSetFocus(wxFocusEvent &);
void OnKillFocus(wxFocusEvent &);
void OnContextMenu(wxContextMenuEvent & WXUNUSED(event)); void OnContextMenu(wxContextMenuEvent & WXUNUSED(event));
void OnTogglePinnedState(wxCommandEvent & event);
bool mPlayRegionDragsSelection; bool mPlayRegionDragsSelection;
bool mTimelineToolTip; bool mTimelineToolTip;
bool mQuickPlayEnabled; bool mQuickPlayEnabled;
CaptureState mCaptureState {};
enum MouseEventState { enum MouseEventState {
mesNone, mesNone,
mesDraggingPlayRegionStart, mesDraggingPlayRegionStart,
@ -501,53 +442,14 @@ private:
StatusChoice mPrevZone { StatusChoice::NoChange }; StatusChoice mPrevZone { StatusChoice::NoChange };
struct TabState {
StatusChoice mButton { StatusChoice::FirstButton };
bool mMenu { false };
TabState() {}
TabState(StatusChoice button, bool menu)
: mButton{ button }, mMenu{ menu } {}
bool operator == (const TabState &rhs) const
{ return mButton == rhs.mButton && mMenu == rhs.mMenu; }
bool operator != (const TabState &rhs) const { return !(*this == rhs); }
TabState &operator ++ () {
if (!mMenu)
mMenu = true;
else {
mMenu = false;
if (!IsButton (++mButton))
mButton = StatusChoice::FirstButton;
}
return *this;
}
TabState &operator -- () {
if (mMenu)
mMenu = false;
else {
mMenu = true;
if (!IsButton (--mButton))
mButton = StatusChoice::LastButton;
}
return *this;
}
};
TabState mTabState;
bool mShowScrubbing { true }; bool mShowScrubbing { true };
mutable int mButtonFontSize { -1 };
mutable wxFont mButtonFont;
bool mDoubleClick {};
bool mShowingMenu {};
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()
friend QuickPlayRulerOverlay; friend QuickPlayRulerOverlay;
wxWindow *mButtons[3];
bool mNeedButtonUpdate { true };
}; };
#endif //define __AUDACITY_RULER__ #endif //define __AUDACITY_RULER__