e4a7c9ba5b
... It is not implicitly convertible from wxString, compelling many uses of the new type to fix compilation.
812 lines
23 KiB
C++
812 lines
23 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
ControlToolBar.cpp
|
|
|
|
Dominic Mazzoni
|
|
Shane T. Mueller
|
|
James Crook
|
|
Leland Lucius
|
|
|
|
*******************************************************************//**
|
|
|
|
\class ControlToolBar
|
|
\brief A ToolBar that has the main Transport buttons.
|
|
|
|
In the GUI, this is referred to as "Transport Toolbar", as
|
|
it corresponds to commands in the Transport menu.
|
|
"Control Toolbar" is historic.
|
|
This class, which is a child of Toolbar, creates the
|
|
window containing the Transport (rewind/play/stop/record/ff)
|
|
buttons. The window can be embedded within a
|
|
normal project window, or within a ToolBarFrame.
|
|
|
|
All of the controls in this window were custom-written for
|
|
Audacity - they are not native controls on any platform -
|
|
however, it is intended that the images could be easily
|
|
replaced to allow "skinning" or just customization to
|
|
match the look and feel of each platform.
|
|
|
|
*//*******************************************************************/
|
|
|
|
#include "../Audacity.h" // for USE_* macros
|
|
#include "ControlToolBar.h"
|
|
|
|
#include "../Experimental.h"
|
|
|
|
#include <algorithm>
|
|
#include <cfloat>
|
|
|
|
// For compilers that support precompilation, includes "wx/wx.h".
|
|
#include <wx/wxprec.h>
|
|
|
|
#include <wx/setup.h> // for wxUSE_* macros
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include <wx/app.h>
|
|
#include <wx/dc.h>
|
|
#include <wx/event.h>
|
|
#include <wx/image.h>
|
|
#include <wx/intl.h>
|
|
#include <wx/sizer.h>
|
|
#include <wx/statusbr.h>
|
|
#include <wx/timer.h>
|
|
#endif
|
|
#include <wx/tooltip.h>
|
|
#include <wx/datetime.h>
|
|
|
|
#include "../AColor.h"
|
|
#include "../AllThemeResources.h"
|
|
#include "../AudioIO.h"
|
|
#include "../ImageManipulation.h"
|
|
#include "../Prefs.h"
|
|
#include "../Project.h"
|
|
#include "../ProjectAudioIO.h"
|
|
#include "../ProjectAudioManager.h"
|
|
#include "../ProjectSettings.h"
|
|
#include "../ProjectStatus.h"
|
|
#include "../ProjectWindow.h"
|
|
#include "../Track.h"
|
|
#include "../widgets/AButton.h"
|
|
#include "../widgets/ErrorDialog.h"
|
|
#include "../FileNames.h"
|
|
|
|
#include "../tracks/ui/Scrubbing.h"
|
|
#include "../toolbars/ToolManager.h"
|
|
|
|
IMPLEMENT_CLASS(ControlToolBar, ToolBar);
|
|
|
|
////////////////////////////////////////////////////////////
|
|
/// Methods for ControlToolBar
|
|
////////////////////////////////////////////////////////////
|
|
|
|
BEGIN_EVENT_TABLE(ControlToolBar, ToolBar)
|
|
EVT_CHAR(ControlToolBar::OnKeyEvent)
|
|
EVT_BUTTON(ID_PLAY_BUTTON, ControlToolBar::OnPlay)
|
|
EVT_BUTTON(ID_STOP_BUTTON, ControlToolBar::OnStop)
|
|
EVT_BUTTON(ID_RECORD_BUTTON, ControlToolBar::OnRecord)
|
|
EVT_BUTTON(ID_REW_BUTTON, ControlToolBar::OnRewind)
|
|
EVT_BUTTON(ID_FF_BUTTON, ControlToolBar::OnFF)
|
|
EVT_BUTTON(ID_PAUSE_BUTTON, ControlToolBar::OnPause)
|
|
EVT_IDLE(ControlToolBar::OnIdle)
|
|
END_EVENT_TABLE()
|
|
|
|
static const TranslatableString
|
|
sStatePlay = XO("Playing")
|
|
, sStateStop = XO("Stopped")
|
|
, sStateRecord = XO("Recording")
|
|
, sStatePause = XO("Paused")
|
|
;
|
|
|
|
//Standard constructor
|
|
// This was called "Control" toolbar in the GUI before - now it is "Transport".
|
|
// Note that we use the legacy "Control" string as the section because this
|
|
// gets written to prefs and cannot be changed in prefs to maintain backwards
|
|
// compatibility
|
|
ControlToolBar::ControlToolBar( AudacityProject &project )
|
|
: ToolBar(project, TransportBarID, _("Transport"), wxT("Control"))
|
|
{
|
|
gPrefs->Read(wxT("/GUI/ErgonomicTransportButtons"), &mErgonomicTransportButtons, true);
|
|
mStrLocale = gPrefs->Read(wxT("/Locale/Language"), wxT(""));
|
|
|
|
mSizer = NULL;
|
|
|
|
/* i18n-hint: These are strings for the status bar, and indicate whether Audacity
|
|
is playing or recording or stopped, and whether it is paused. */
|
|
}
|
|
|
|
ControlToolBar::~ControlToolBar()
|
|
{
|
|
}
|
|
|
|
|
|
ControlToolBar *ControlToolBar::Find( AudacityProject &project )
|
|
{
|
|
auto &toolManager = ToolManager::Get( project );
|
|
return static_cast<ControlToolBar*>(
|
|
toolManager.GetToolBar(TransportBarID) );
|
|
}
|
|
|
|
ControlToolBar &ControlToolBar::Get( AudacityProject &project )
|
|
{
|
|
auto &toolManager = ToolManager::Get( project );
|
|
return *static_cast<ControlToolBar*>(
|
|
toolManager.GetToolBar(TransportBarID) );
|
|
}
|
|
|
|
const ControlToolBar &ControlToolBar::Get( const AudacityProject &project )
|
|
{
|
|
return Get( const_cast<AudacityProject&>( project )) ;
|
|
}
|
|
|
|
void ControlToolBar::Create(wxWindow * parent)
|
|
{
|
|
ToolBar::Create(parent);
|
|
UpdatePrefs();
|
|
}
|
|
|
|
// This is a convenience function that allows for button creation in
|
|
// MakeButtons() with fewer arguments
|
|
AButton *ControlToolBar::MakeButton(ControlToolBar *pBar,
|
|
teBmps eEnabledUp, teBmps eEnabledDown, teBmps eDisabled,
|
|
int id,
|
|
bool processdownevents,
|
|
const wxChar *label)
|
|
{
|
|
AButton *r = ToolBar::MakeButton(pBar,
|
|
bmpRecoloredUpLarge, bmpRecoloredDownLarge, bmpRecoloredUpHiliteLarge, bmpRecoloredHiliteLarge,
|
|
eEnabledUp, eEnabledDown, eDisabled,
|
|
wxWindowID(id),
|
|
wxDefaultPosition, processdownevents,
|
|
theTheme.ImageSize( bmpRecoloredUpLarge ));
|
|
r->SetLabel(label);
|
|
enum { deflation =
|
|
#ifdef __WXMAC__
|
|
6
|
|
#else
|
|
12
|
|
#endif
|
|
};
|
|
r->SetFocusRect( r->GetClientRect().Deflate( deflation, deflation ) );
|
|
|
|
return r;
|
|
}
|
|
|
|
// static
|
|
void ControlToolBar::MakeAlternateImages(AButton &button, int idx,
|
|
teBmps eEnabledUp,
|
|
teBmps eEnabledDown,
|
|
teBmps eDisabled)
|
|
{
|
|
ToolBar::MakeAlternateImages(button, idx,
|
|
bmpRecoloredUpLarge, bmpRecoloredDownLarge, bmpRecoloredUpHiliteLarge, bmpRecoloredHiliteLarge,
|
|
eEnabledUp, eEnabledDown, eDisabled,
|
|
theTheme.ImageSize( bmpRecoloredUpLarge ));
|
|
}
|
|
|
|
void ControlToolBar::Populate()
|
|
{
|
|
SetBackgroundColour( theTheme.Colour( clrMedium ) );
|
|
MakeButtonBackgroundsLarge();
|
|
|
|
mPause = MakeButton(this, bmpPause, bmpPause, bmpPauseDisabled,
|
|
ID_PAUSE_BUTTON, true, _("Pause"));
|
|
|
|
mPlay = MakeButton(this, bmpPlay, bmpPlay, bmpPlayDisabled,
|
|
ID_PLAY_BUTTON, true, _("Play"));
|
|
MakeAlternateImages(*mPlay, 1, bmpLoop, bmpLoop, bmpLoopDisabled);
|
|
MakeAlternateImages(*mPlay, 2,
|
|
bmpCutPreview, bmpCutPreview, bmpCutPreviewDisabled);
|
|
MakeAlternateImages(*mPlay, 3,
|
|
bmpScrub, bmpScrub, bmpScrubDisabled);
|
|
MakeAlternateImages(*mPlay, 4,
|
|
bmpSeek, bmpSeek, bmpSeekDisabled);
|
|
mPlay->FollowModifierKeys();
|
|
|
|
mStop = MakeButton(this, bmpStop, bmpStop, bmpStopDisabled ,
|
|
ID_STOP_BUTTON, false, _("Stop"));
|
|
|
|
mRewind = MakeButton(this, bmpRewind, bmpRewind, bmpRewindDisabled,
|
|
ID_REW_BUTTON, false, _("Skip to Start"));
|
|
|
|
mFF = MakeButton(this, bmpFFwd, bmpFFwd, bmpFFwdDisabled,
|
|
ID_FF_BUTTON, false, _("Skip to End"));
|
|
|
|
mRecord = MakeButton(this, bmpRecord, bmpRecord, bmpRecordDisabled,
|
|
ID_RECORD_BUTTON, false, _("Record"));
|
|
|
|
bool bPreferNewTrack;
|
|
gPrefs->Read("/GUI/PreferNewTrackRecord",&bPreferNewTrack, false);
|
|
if( !bPreferNewTrack )
|
|
MakeAlternateImages(*mRecord, 1, bmpRecordBelow, bmpRecordBelow,
|
|
bmpRecordBelowDisabled);
|
|
else
|
|
MakeAlternateImages(*mRecord, 1, bmpRecordBeside, bmpRecordBeside,
|
|
bmpRecordBesideDisabled);
|
|
|
|
mRecord->FollowModifierKeys();
|
|
|
|
#if wxUSE_TOOLTIPS
|
|
RegenerateTooltips();
|
|
wxToolTip::Enable(true);
|
|
wxToolTip::SetDelay(1000);
|
|
#endif
|
|
|
|
// Set default order and mode
|
|
ArrangeButtons();
|
|
}
|
|
|
|
void ControlToolBar::RegenerateTooltips()
|
|
{
|
|
#if wxUSE_TOOLTIPS
|
|
for (long iWinID = ID_PAUSE_BUTTON; iWinID < BUTTON_COUNT; iWinID++)
|
|
{
|
|
auto pCtrl = static_cast<AButton*>(this->FindWindow(iWinID));
|
|
CommandID name;
|
|
switch (iWinID)
|
|
{
|
|
case ID_PLAY_BUTTON:
|
|
// Without shift
|
|
name = wxT("PlayStop");
|
|
break;
|
|
case ID_RECORD_BUTTON:
|
|
// Without shift
|
|
//name = wxT("Record");
|
|
name = wxT("Record1stChoice");
|
|
break;
|
|
case ID_PAUSE_BUTTON:
|
|
name = wxT("Pause");
|
|
break;
|
|
case ID_STOP_BUTTON:
|
|
name = wxT("Stop");
|
|
break;
|
|
case ID_FF_BUTTON:
|
|
name = wxT("CursProjectEnd");
|
|
break;
|
|
case ID_REW_BUTTON:
|
|
name = wxT("CursProjectStart");
|
|
break;
|
|
}
|
|
std::vector<TranslatedInternalString> commands(
|
|
1u, { name, pCtrl->GetLabel() } );
|
|
|
|
// Some have a second
|
|
switch (iWinID)
|
|
{
|
|
case ID_PLAY_BUTTON:
|
|
// With shift
|
|
commands.push_back( { wxT("PlayLooped"), _("Loop Play") } );
|
|
break;
|
|
case ID_RECORD_BUTTON:
|
|
// With shift
|
|
{ bool bPreferNewTrack;
|
|
gPrefs->Read("/GUI/PreferNewTrackRecord",&bPreferNewTrack, false);
|
|
// For the shortcut tooltip.
|
|
commands.push_back( {
|
|
wxT("Record2ndChoice"),
|
|
!bPreferNewTrack
|
|
? _("Record New Track")
|
|
: _("Append Record")
|
|
} );
|
|
}
|
|
break;
|
|
case ID_PAUSE_BUTTON:
|
|
break;
|
|
case ID_STOP_BUTTON:
|
|
break;
|
|
case ID_FF_BUTTON:
|
|
// With shift
|
|
commands.push_back( {
|
|
wxT("SelEnd"), _("Select to End") } );
|
|
break;
|
|
case ID_REW_BUTTON:
|
|
// With shift
|
|
commands.push_back( {
|
|
wxT("SelStart"), _("Select to Start") } );
|
|
break;
|
|
}
|
|
ToolBar::SetButtonToolTip(
|
|
mProject, *pCtrl, commands.data(), commands.size());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ControlToolBar::UpdatePrefs()
|
|
{
|
|
bool updated = false;
|
|
bool active;
|
|
|
|
gPrefs->Read( wxT("/GUI/ErgonomicTransportButtons"), &active, true );
|
|
if( mErgonomicTransportButtons != active )
|
|
{
|
|
mErgonomicTransportButtons = active;
|
|
updated = true;
|
|
}
|
|
wxString strLocale = gPrefs->Read(wxT("/Locale/Language"), wxT(""));
|
|
if (mStrLocale != strLocale)
|
|
{
|
|
mStrLocale = strLocale;
|
|
updated = true;
|
|
}
|
|
|
|
if( updated )
|
|
{
|
|
ReCreateButtons(); // side effect: calls RegenerateTooltips()
|
|
Updated();
|
|
}
|
|
else
|
|
// The other reason to regenerate tooltips is if keyboard shortcuts for
|
|
// transport buttons changed, but that's too much work to check for, so just
|
|
// always do it. (Much cheaper than calling ReCreateButtons() in all cases.
|
|
RegenerateTooltips();
|
|
|
|
|
|
// Set label to pull in language change
|
|
SetLabel(_("Transport"));
|
|
|
|
// Give base class a chance
|
|
ToolBar::UpdatePrefs();
|
|
}
|
|
|
|
void ControlToolBar::ArrangeButtons()
|
|
{
|
|
int flags = wxALIGN_CENTER | wxRIGHT;
|
|
|
|
// (Re)allocate the button sizer
|
|
if( mSizer )
|
|
{
|
|
Detach( mSizer );
|
|
std::unique_ptr < wxSizer > {mSizer}; // DELETE it
|
|
}
|
|
|
|
Add((mSizer = safenew wxBoxSizer(wxHORIZONTAL)), 1, wxEXPAND);
|
|
|
|
// Start with a little extra space
|
|
mSizer->Add( 5, 55 );
|
|
|
|
// Add the buttons in order based on ergonomic setting
|
|
if( mErgonomicTransportButtons )
|
|
{
|
|
mPause->MoveBeforeInTabOrder( mRecord );
|
|
mPlay->MoveBeforeInTabOrder( mRecord );
|
|
mStop->MoveBeforeInTabOrder( mRecord );
|
|
mRewind->MoveBeforeInTabOrder( mRecord );
|
|
mFF->MoveBeforeInTabOrder( mRecord );
|
|
|
|
mSizer->Add( mPause, 0, flags, 2 );
|
|
mSizer->Add( mPlay, 0, flags, 2 );
|
|
mSizer->Add( mStop, 0, flags, 2 );
|
|
mSizer->Add( mRewind, 0, flags, 2 );
|
|
mSizer->Add( mFF, 0, flags, 10 );
|
|
mSizer->Add( mRecord, 0, flags, 5 );
|
|
}
|
|
else
|
|
{
|
|
mRewind->MoveBeforeInTabOrder( mFF );
|
|
mPlay->MoveBeforeInTabOrder( mFF );
|
|
mRecord->MoveBeforeInTabOrder( mFF );
|
|
mPause->MoveBeforeInTabOrder( mFF );
|
|
mStop->MoveBeforeInTabOrder( mFF );
|
|
|
|
mSizer->Add( mRewind, 0, flags, 2 );
|
|
mSizer->Add( mPlay, 0, flags, 2 );
|
|
mSizer->Add( mRecord, 0, flags, 2 );
|
|
mSizer->Add( mPause, 0, flags, 2 );
|
|
mSizer->Add( mStop, 0, flags, 2 );
|
|
mSizer->Add( mFF, 0, flags, 5 );
|
|
}
|
|
|
|
// Layout the sizer
|
|
mSizer->Layout();
|
|
|
|
// Layout the toolbar
|
|
Layout();
|
|
|
|
// (Re)Establish the minimum size
|
|
SetMinSize( GetSizer()->GetMinSize() );
|
|
}
|
|
|
|
void ControlToolBar::ReCreateButtons()
|
|
{
|
|
bool playDown = false;
|
|
bool playShift = false;
|
|
bool pauseDown = false;
|
|
bool recordDown = false;
|
|
bool recordShift = false;
|
|
|
|
// ToolBar::ReCreateButtons() will get rid of its sizer and
|
|
// since we've attached our sizer to it, ours will get deleted too
|
|
// so clean ours up first.
|
|
if( mSizer )
|
|
{
|
|
playDown = mPlay->IsDown();
|
|
playShift = mPlay->WasShiftDown();
|
|
pauseDown = mPause->IsDown();
|
|
recordDown = mRecord->IsDown();
|
|
recordShift = mRecord->WasShiftDown();
|
|
Detach( mSizer );
|
|
|
|
std::unique_ptr < wxSizer > {mSizer}; // DELETE it
|
|
mSizer = NULL;
|
|
}
|
|
|
|
ToolBar::ReCreateButtons();
|
|
|
|
if (playDown)
|
|
{
|
|
ControlToolBar::PlayAppearance appearance =
|
|
playShift ? ControlToolBar::PlayAppearance::Looped
|
|
: ControlToolBar::PlayAppearance::Straight;
|
|
SetPlay(playDown, appearance);
|
|
}
|
|
|
|
if (pauseDown)
|
|
{
|
|
mPause->PushDown();
|
|
}
|
|
|
|
if (recordDown)
|
|
{
|
|
mRecord->SetAlternateIdx(recordShift ? 1 : 0);
|
|
mRecord->PushDown();
|
|
}
|
|
|
|
EnableDisableButtons();
|
|
|
|
RegenerateTooltips();
|
|
}
|
|
|
|
void ControlToolBar::Repaint( wxDC *dc )
|
|
{
|
|
#ifndef USE_AQUA_THEME
|
|
wxSize s = mSizer->GetSize();
|
|
wxPoint p = mSizer->GetPosition();
|
|
|
|
wxRect bevelRect( p.x, p.y, s.GetWidth() - 1, s.GetHeight() - 1 );
|
|
AColor::Bevel( *dc, true, bevelRect );
|
|
#endif
|
|
}
|
|
|
|
void ControlToolBar::EnableDisableButtons()
|
|
{
|
|
AudacityProject *p = &mProject;
|
|
auto &projectAudioManager = ProjectAudioManager::Get( mProject );
|
|
bool canStop = projectAudioManager.CanStopAudioStream();
|
|
|
|
bool paused = mPause->IsDown();
|
|
bool playing = mPlay->IsDown();
|
|
bool recording = mRecord->IsDown();
|
|
auto gAudioIO = AudioIO::Get();
|
|
bool busy = gAudioIO->IsBusy();
|
|
|
|
// Only interested in audio type tracks
|
|
bool tracks = p && TrackList::Get( *p ).Any<AudioTrack>(); // PRL: PlayableTrack ?
|
|
|
|
mPlay->SetEnabled( canStop && tracks && !recording );
|
|
mRecord->SetEnabled(
|
|
canStop &&
|
|
!(busy && !recording && !paused) &&
|
|
!(playing && !paused)
|
|
);
|
|
mStop->SetEnabled(canStop && (playing || recording));
|
|
mRewind->SetEnabled(paused || (!playing && !recording));
|
|
mFF->SetEnabled(tracks && (paused || (!playing && !recording)));
|
|
|
|
mPause->SetEnabled(canStop);
|
|
}
|
|
|
|
void ControlToolBar::SetPlay(bool down, PlayAppearance appearance)
|
|
{
|
|
if (down) {
|
|
mPlay->SetShift(appearance == PlayAppearance::Looped);
|
|
mPlay->SetControl(appearance == PlayAppearance::CutPreview);
|
|
mPlay->SetAlternateIdx(static_cast<int>(appearance));
|
|
mPlay->PushDown();
|
|
}
|
|
else {
|
|
mPlay->PopUp();
|
|
mPlay->SetAlternateIdx(0);
|
|
}
|
|
EnableDisableButtons();
|
|
}
|
|
|
|
void ControlToolBar::SetStop()
|
|
{
|
|
mStop->PushDown();
|
|
EnableDisableButtons();
|
|
}
|
|
|
|
void ControlToolBar::OnKeyEvent(wxKeyEvent & event)
|
|
{
|
|
// PRL: is this handler really ever reached? Is the ControlToolBar ever
|
|
// focused? Isn't there a global event filter that interprets the spacebar
|
|
// key (or other key chosen in preferences) and dispatches to DoPlayStop,
|
|
// according to CommandManager's table, before we come to this redundant
|
|
// function?
|
|
|
|
if (event.ControlDown() || event.AltDown()) {
|
|
event.Skip();
|
|
return;
|
|
}
|
|
|
|
auto gAudioIO = AudioIOBase::Get();
|
|
auto &projectAudioManager = ProjectAudioManager::Get( mProject );
|
|
|
|
// Does not appear to be needed on Linux. Perhaps on some other platform?
|
|
// If so, "!CanStopAudioStream()" should probably apply.
|
|
if (event.GetKeyCode() == WXK_SPACE) {
|
|
if ( projectAudioManager.Playing() || projectAudioManager.Recording() ) {
|
|
SetStop();
|
|
projectAudioManager.Stop();
|
|
}
|
|
else if (!gAudioIO->IsBusy()) {
|
|
projectAudioManager.PlayCurrentRegion();
|
|
}
|
|
return;
|
|
}
|
|
event.Skip();
|
|
}
|
|
|
|
void ControlToolBar::OnPlay(wxCommandEvent & WXUNUSED(evt))
|
|
{
|
|
auto p = &mProject;
|
|
auto &projectAudioManager = ProjectAudioManager::Get( mProject );
|
|
bool canStop = projectAudioManager.CanStopAudioStream();
|
|
|
|
if ( !canStop )
|
|
return;
|
|
|
|
projectAudioManager.Stop();
|
|
|
|
PlayDefault();
|
|
}
|
|
|
|
void ControlToolBar::OnStop(wxCommandEvent & WXUNUSED(evt))
|
|
{
|
|
auto &projectAudioManager = ProjectAudioManager::Get( mProject );
|
|
bool canStop = projectAudioManager.CanStopAudioStream();
|
|
|
|
if ( canStop ) {
|
|
projectAudioManager.Stop();
|
|
}
|
|
}
|
|
|
|
void ControlToolBar::PlayDefault()
|
|
{
|
|
// Let control have precedence over shift
|
|
const bool cutPreview = mPlay->WasControlDown();
|
|
const bool looped = !cutPreview &&
|
|
mPlay->WasShiftDown();
|
|
ProjectAudioManager::Get( mProject ).PlayCurrentRegion(looped, cutPreview);
|
|
}
|
|
|
|
void ControlToolBar::OnRecord(wxCommandEvent &evt)
|
|
// STRONG-GUARANTEE (for state of current project's tracks)
|
|
{
|
|
// TODO: It would be neater if Menu items and Toolbar buttons used the same code for
|
|
// enabling/disabling, and all fell into the same action routines.
|
|
// Here instead we reduplicate some logic (from CommandHandler) because it isn't
|
|
// normally used for buttons.
|
|
|
|
bool altAppearance = mRecord->WasShiftDown();
|
|
ProjectAudioManager::Get( mProject ).OnRecord( altAppearance );
|
|
}
|
|
|
|
void ControlToolBar::OnPause(wxCommandEvent & WXUNUSED(evt))
|
|
{
|
|
ProjectAudioManager::Get( mProject ).OnPause();
|
|
}
|
|
|
|
void ControlToolBar::OnIdle(wxIdleEvent & event)
|
|
{
|
|
event.Skip();
|
|
|
|
auto &projectAudioManager = ProjectAudioManager::Get( mProject );
|
|
if ( projectAudioManager.Paused() )
|
|
mPause->PushDown();
|
|
else
|
|
mPause->PopUp();
|
|
|
|
bool recording = projectAudioManager.Recording();
|
|
if (!recording) {
|
|
mRecord->PopUp();
|
|
mRecord->SetAlternateIdx( wxGetKeyState(WXK_SHIFT) ? 1 : 0 );
|
|
}
|
|
else {
|
|
mRecord->PushDown();
|
|
mRecord->SetAlternateIdx( projectAudioManager.Appending() ? 0 : 1 );
|
|
}
|
|
|
|
bool playing = projectAudioManager.Playing();
|
|
if ( !(playing || Scrubber::Get(mProject).HasMark()) ) {
|
|
mPlay->PopUp();
|
|
mPlay->SetAlternateIdx(
|
|
wxGetKeyState(WXK_CONTROL)
|
|
? 2
|
|
: wxGetKeyState(WXK_SHIFT)
|
|
? 1
|
|
: 0
|
|
);
|
|
}
|
|
else {
|
|
mPlay->PushDown();
|
|
mPlay->SetAlternateIdx(
|
|
projectAudioManager.Cutting()
|
|
? 2
|
|
: projectAudioManager.Looping()
|
|
? 1
|
|
: 0
|
|
);
|
|
}
|
|
|
|
if ( recording || playing )
|
|
StartScrollingIfPreferred();
|
|
else
|
|
StopScrolling();
|
|
|
|
if ( projectAudioManager.Stopping() )
|
|
mStop->PushDown();
|
|
else
|
|
// push-downs of the stop button are only momentary and always pop up now
|
|
mStop->PopUp();
|
|
|
|
UpdateStatusBar();
|
|
EnableDisableButtons();
|
|
}
|
|
|
|
void ControlToolBar::OnRewind(wxCommandEvent & WXUNUSED(evt))
|
|
{
|
|
mRewind->PushDown();
|
|
mRewind->PopUp();
|
|
|
|
AudacityProject *p = &mProject;
|
|
{
|
|
ProjectAudioManager::Get( *p ).StopIfPaused();
|
|
ProjectWindow::Get( *p ).Rewind(mRewind->WasShiftDown());
|
|
}
|
|
}
|
|
|
|
void ControlToolBar::OnFF(wxCommandEvent & WXUNUSED(evt))
|
|
{
|
|
mFF->PushDown();
|
|
mFF->PopUp();
|
|
|
|
AudacityProject *p = &mProject;
|
|
|
|
{
|
|
ProjectAudioManager::Get( *p ).StopIfPaused();
|
|
ProjectWindow::Get( *p ).SkipEnd(mFF->WasShiftDown());
|
|
}
|
|
}
|
|
|
|
// works out the width of the field in the status bar needed for the state (eg play, record pause)
|
|
static ProjectStatus::RegisteredStatusWidthFunction
|
|
registeredStatusWidthFunction{
|
|
[]( const AudacityProject &, StatusBarField field )
|
|
-> ProjectStatus::StatusWidthResult
|
|
{
|
|
if ( field == stateStatusBarField ) {
|
|
const auto pauseString = wxT(" ") + GetCustomTranslation(sStatePause);
|
|
|
|
std::vector<wxString> strings;
|
|
for ( auto pString :
|
|
{ &sStatePlay, &sStateStop, &sStateRecord } )
|
|
{
|
|
strings.push_back(
|
|
GetCustomTranslation(*pString) + pauseString + wxT(".") );
|
|
}
|
|
|
|
// added constant needed because xMax isn't large enough for some reason, plus some space.
|
|
return { std::move( strings ), 30 };
|
|
}
|
|
return {};
|
|
}
|
|
};
|
|
|
|
wxString ControlToolBar::StateForStatusBar()
|
|
{
|
|
wxString state;
|
|
auto &projectAudioManager = ProjectAudioManager::Get( mProject );
|
|
|
|
auto pProject = &mProject;
|
|
auto scrubState = pProject
|
|
? Scrubber::Get( *pProject ).GetUntranslatedStateString()
|
|
: TranslatableString{};
|
|
if (!scrubState.empty())
|
|
state = scrubState.Translation();
|
|
else if (mPlay->IsDown())
|
|
state = wxGetTranslation(sStatePlay);
|
|
else if (projectAudioManager.Recording())
|
|
state = wxGetTranslation(sStateRecord);
|
|
else
|
|
state = wxGetTranslation(sStateStop);
|
|
|
|
if (mPause->IsDown())
|
|
{
|
|
state.Append(wxT(" "));
|
|
state.Append(wxGetTranslation(sStatePause));
|
|
}
|
|
|
|
state.Append(wxT("."));
|
|
|
|
return state;
|
|
}
|
|
|
|
void ControlToolBar::UpdateStatusBar()
|
|
{
|
|
ProjectStatus::Get( mProject ).Set(
|
|
StateForStatusBar(), stateStatusBarField
|
|
);
|
|
}
|
|
|
|
void ControlToolBar::StartScrollingIfPreferred()
|
|
{
|
|
if ( Scrubber::Get( mProject ).IsTransportingPinned() )
|
|
StartScrolling();
|
|
#ifdef __WXMAC__
|
|
else if (Scrubber::Get( mProject ).HasMark()) {
|
|
// 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.
|
|
ProjectWindow::Get( mProject ).GetPlaybackScroller().Activate
|
|
(ProjectWindow::PlaybackScroller::Mode::Refresh);
|
|
}
|
|
#endif
|
|
else
|
|
StopScrolling();
|
|
}
|
|
|
|
void ControlToolBar::StartScrolling()
|
|
{
|
|
using Mode = ProjectWindow::PlaybackScroller::Mode;
|
|
const auto project = &mProject;
|
|
if (project) {
|
|
auto mode = Mode::Pinned;
|
|
|
|
#if 0
|
|
// Enable these lines to pin the playhead right instead of center,
|
|
// when recording but not overdubbing.
|
|
auto gAudioIO = AudioIO::Get();
|
|
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;
|
|
#ifdef EXPERIMENTAL_DA
|
|
gPrefs->Read(wxT("/AudioIO/Duplex"), &duplex, false);
|
|
#else
|
|
gPrefs->Read(wxT("/AudioIO/Duplex"), &duplex, true);
|
|
#endif
|
|
if (duplex) {
|
|
// See if there is really anything being overdubbed
|
|
if (gAudioIO->GetNumPlaybackChannels() == 0)
|
|
// No.
|
|
duplex = false;
|
|
}
|
|
|
|
if (!duplex)
|
|
mode = Mode::Right;
|
|
}
|
|
#endif
|
|
|
|
ProjectWindow::Get( *project ).GetPlaybackScroller().Activate(mode);
|
|
}
|
|
}
|
|
|
|
void ControlToolBar::StopScrolling()
|
|
{
|
|
const auto project = &mProject;
|
|
if(project)
|
|
ProjectWindow::Get( *project ).GetPlaybackScroller().Activate
|
|
(ProjectWindow::PlaybackScroller::Mode::Off);
|
|
}
|
|
|
|
static RegisteredToolbarFactory factory{ TransportBarID,
|
|
[]( AudacityProject &project ){
|
|
return ToolBar::Holder{ safenew ControlToolBar{ project } }; }
|
|
};
|