1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-16 16:10:06 +02:00

Fix for http://bugzilla.audacityteam.org/show_bug.cgi?id=643 "Residual consistency issues with SHIFT showing Loop Play button icon" by Paul Licameli.

* Loop play-at-speed and cut preview play-at-speed implemented.
* Shift or ctrl down now affect all relevant buttons, loop or cut preview, normal or at speed, and append-record.
This commit is contained in:
james.k.crook@gmail.com 2014-11-29 17:22:05 +00:00
parent 30500b2e85
commit 7324997db6
14 changed files with 4503 additions and 4231 deletions

View File

@ -62,6 +62,10 @@ from there. Audacity will look for a file called "Pause.png".
DEFINE_IMAGE( bmpFFwdDisabled, wxImage( 16, 16 ), wxT("FFwdDisabled")); DEFINE_IMAGE( bmpFFwdDisabled, wxImage( 16, 16 ), wxT("FFwdDisabled"));
DEFINE_IMAGE( bmpRecord, wxImage( 16, 16 ), wxT("Record")); DEFINE_IMAGE( bmpRecord, wxImage( 16, 16 ), wxT("Record"));
DEFINE_IMAGE( bmpRecordDisabled, wxImage( 16, 16 ), wxT("RecordDisabled")); DEFINE_IMAGE( bmpRecordDisabled, wxImage( 16, 16 ), wxT("RecordDisabled"));
DEFINE_IMAGE( bmpCutPreview, wxImage( 16, 16 ), wxT("CutPreview"));
DEFINE_IMAGE( bmpCutPreviewDisabled, wxImage( 16, 16 ), wxT("CutPreviewDisabled"));
DEFINE_IMAGE( bmpAppendRecord, wxImage( 16, 16 ), wxT("AppendRecord"));
DEFINE_IMAGE( bmpAppendRecordDisabled, wxImage( 16, 16 ), wxT("AppendRecordDisabled"));
SET_THEME_FLAGS( resFlagNewLine ); SET_THEME_FLAGS( resFlagNewLine );
DEFINE_IMAGE( bmpUpButtonLarge, wxImage( 48, 48 ), wxT("UpButtonLarge")); DEFINE_IMAGE( bmpUpButtonLarge, wxImage( 48, 48 ), wxT("UpButtonLarge"));

View File

@ -1281,6 +1281,8 @@ void AudacityProject::CreateMenusAndCommands()
c->AddCommand(wxT("InputGainDec"), _("Decrease recording volume"), FN(OnInputGainDec)); c->AddCommand(wxT("InputGainDec"), _("Decrease recording volume"), FN(OnInputGainDec));
c->AddCommand(wxT("PlayAtSpeed"), _("Play at speed"), FN(OnPlayAtSpeed)); c->AddCommand(wxT("PlayAtSpeed"), _("Play at speed"), FN(OnPlayAtSpeed));
c->AddCommand(wxT("PlayAtSpeedLooped"), _("Loop Play at speed"), FN(OnPlayAtSpeedLooped));
c->AddCommand(wxT("PlayAtSpeedCutPreview"), _("Play Cut Preview at speed"), FN(OnPlayAtSpeedCutPreview));
c->AddCommand(wxT("SetPlaySpeed"), _("Adjust playback speed"), FN(OnSetPlaySpeed)); c->AddCommand(wxT("SetPlaySpeed"), _("Adjust playback speed"), FN(OnSetPlaySpeed));
c->AddCommand(wxT("PlaySpeedInc"), _("Increase playback speed"), FN(OnPlaySpeedInc)); c->AddCommand(wxT("PlaySpeedInc"), _("Increase playback speed"), FN(OnPlaySpeedInc));
c->AddCommand(wxT("PlaySpeedDec"), _("Decrease playback speed"), FN(OnPlaySpeedDec)); c->AddCommand(wxT("PlaySpeedDec"), _("Decrease playback speed"), FN(OnPlaySpeedDec));
@ -3004,7 +3006,23 @@ void AudacityProject::OnPlayAtSpeed()
{ {
TranscriptionToolBar *tb = GetTranscriptionToolBar(); TranscriptionToolBar *tb = GetTranscriptionToolBar();
if (tb) { if (tb) {
tb->PlayAtSpeed(); tb->PlayAtSpeed(false, false);
}
}
void AudacityProject::OnPlayAtSpeedLooped()
{
TranscriptionToolBar *tb = GetTranscriptionToolBar();
if (tb) {
tb->PlayAtSpeed(true, false);
}
}
void AudacityProject::OnPlayAtSpeedCutPreview()
{
TranscriptionToolBar *tb = GetTranscriptionToolBar();
if (tb) {
tb->PlayAtSpeed(false, true);
} }
} }

View File

@ -127,6 +127,8 @@ void OnInputGainDec();
// Transcription control // Transcription control
void OnPlayAtSpeed(); void OnPlayAtSpeed();
void OnPlayAtSpeedLooped();
void OnPlayAtSpeedCutPreview();
void OnSetPlaySpeed(); void OnSetPlaySpeed();
void OnPlaySpeedInc(); void OnPlaySpeedInc();
void OnPlaySpeedDec(); void OnPlaySpeedDec();

View File

@ -277,6 +277,7 @@ MixerTrackCluster::MixerTrackCluster(wxWindow* parent,
true); // toggle button true); // toggle button
mToggleButton_Mute->SetName(_("Mute")); mToggleButton_Mute->SetName(_("Mute"));
mToggleButton_Mute->SetAlternateImages( mToggleButton_Mute->SetAlternateImages(
1,
*(mMixerBoard->mImageMuteUp), *(mMixerBoard->mImageMuteOver), *(mMixerBoard->mImageMuteUp), *(mMixerBoard->mImageMuteOver),
*(mMixerBoard->mImageMuteDown), *(mMixerBoard->mImageMuteDisabled)); *(mMixerBoard->mImageMuteDown), *(mMixerBoard->mImageMuteDisabled));
this->UpdateMute(); this->UpdateMute();
@ -465,10 +466,10 @@ void MixerTrackCluster::UpdateName()
void MixerTrackCluster::UpdateMute() void MixerTrackCluster::UpdateMute()
{ {
#ifdef EXPERIMENTAL_MIDI_OUT #ifdef EXPERIMENTAL_MIDI_OUT
mToggleButton_Mute->SetAlternate(mTrack->GetSolo()); mToggleButton_Mute->SetAlternateIdx(mTrack->GetSolo() ? 1 : 0);
if (mTrack->GetMute()) if (mTrack->GetMute())
#else #else
mToggleButton_Mute->SetAlternate(mLeftTrack->GetSolo()); mToggleButton_Mute->SetAlternateIdx(mLeftTrack->GetSolo() ? 1 : 0);
if (mLeftTrack->GetMute()) if (mLeftTrack->GetMute())
#endif #endif
mToggleButton_Mute->PushDown(); mToggleButton_Mute->PushDown();
@ -487,7 +488,7 @@ void MixerTrackCluster::UpdateSolo()
mToggleButton_Solo->PushDown(); mToggleButton_Solo->PushDown();
else else
mToggleButton_Solo->PopUp(); mToggleButton_Solo->PopUp();
mToggleButton_Mute->SetAlternate(bIsSolo); mToggleButton_Mute->SetAlternateIdx(bIsSolo ? 1 : 0);
} }
void MixerTrackCluster::UpdatePan() void MixerTrackCluster::UpdatePan()
@ -827,10 +828,10 @@ void MixerTrackCluster::OnButton_Mute(wxCommandEvent& WXUNUSED(event))
{ {
#ifdef EXPERIMENTAL_MIDI_OUT #ifdef EXPERIMENTAL_MIDI_OUT
mProject->HandleTrackMute(mTrack, mToggleButton_Mute->WasShiftDown()); mProject->HandleTrackMute(mTrack, mToggleButton_Mute->WasShiftDown());
mToggleButton_Mute->SetAlternate(mTrack->GetSolo()); mToggleButton_Mute->SetAlternateIdx(mTrack->GetSolo() ? 1 : 0);
#else #else
mProject->HandleTrackMute(mLeftTrack, mToggleButton_Mute->WasShiftDown()); mProject->HandleTrackMute(mLeftTrack, mToggleButton_Mute->WasShiftDown());
mToggleButton_Mute->SetAlternate(mLeftTrack->GetSolo()); mToggleButton_Mute->SetAlternateIdx(mLeftTrack->GetSolo() ? 1 : 0);
#endif #endif
// Update the TrackPanel correspondingly. // Update the TrackPanel correspondingly.
@ -858,7 +859,7 @@ void MixerTrackCluster::OnButton_Solo(wxCommandEvent& WXUNUSED(event))
mProject->HandleTrackSolo(mLeftTrack, mToggleButton_Solo->WasShiftDown()); mProject->HandleTrackSolo(mLeftTrack, mToggleButton_Solo->WasShiftDown());
bool bIsSolo = mLeftTrack->GetSolo(); bool bIsSolo = mLeftTrack->GetSolo();
#endif #endif
mToggleButton_Mute->SetAlternate(bIsSolo); mToggleButton_Mute->SetAlternateIdx(bIsSolo ? 1 : 0);
// Update the TrackPanel correspondingly. // Update the TrackPanel correspondingly.
if (mProject->IsSoloSimple()) if (mProject->IsSoloSimple())

View File

@ -63,7 +63,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/ControlToolBar.h" #include "toolbars/ToolManager.h"
#include "ImageManipulation.h" #include "ImageManipulation.h"
#include "Theme.h" #include "Theme.h"
#include "Experimental.h" #include "Experimental.h"
@ -223,9 +223,11 @@ void Theme::EnsureInitialised()
void Theme::ApplyUpdatedImages() void Theme::ApplyUpdatedImages()
{ {
AudacityProject *p = GetActiveProject(); AudacityProject *p = GetActiveProject();
if( p->GetControlToolBar() ) for( int ii = 0; ii < ToolBarCount; ++ii )
{ {
p->GetControlToolBar()->ReCreateButtons(); ToolBar *pToolBar = p->mToolManager->GetToolBar(ii);
if( pToolBar )
pToolBar->ReCreateButtons();
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -49,6 +49,7 @@
#include <wx/tooltip.h> #include <wx/tooltip.h>
#include "ControlToolBar.h" #include "ControlToolBar.h"
#include "TranscriptionToolBar.h"
#include "MeterToolBar.h" #include "MeterToolBar.h"
#include "../AColor.h" #include "../AColor.h"
@ -72,7 +73,6 @@ AudacityProject *ControlToolBar::mBusyProject = NULL;
BEGIN_EVENT_TABLE(ControlToolBar, ToolBar) BEGIN_EVENT_TABLE(ControlToolBar, ToolBar)
EVT_CHAR(ControlToolBar::OnKeyEvent) EVT_CHAR(ControlToolBar::OnKeyEvent)
EVT_TIMER(wxID_ANY, ControlToolBar::OnTimer)
EVT_BUTTON(ID_PLAY_BUTTON, ControlToolBar::OnPlay) EVT_BUTTON(ID_PLAY_BUTTON, ControlToolBar::OnPlay)
EVT_BUTTON(ID_STOP_BUTTON, ControlToolBar::OnStop) EVT_BUTTON(ID_STOP_BUTTON, ControlToolBar::OnStop)
EVT_BUTTON(ID_RECORD_BUTTON, ControlToolBar::OnRecord) EVT_BUTTON(ID_RECORD_BUTTON, ControlToolBar::OnRecord)
@ -89,8 +89,6 @@ END_EVENT_TABLE()
ControlToolBar::ControlToolBar() ControlToolBar::ControlToolBar()
: ToolBar(TransportBarID, _("Transport"), wxT("Control")) : ToolBar(TransportBarID, _("Transport"), wxT("Control"))
{ {
mShiftKeyTimer.SetOwner(this);
mPaused = false; mPaused = false;
gPrefs->Read(wxT("/GUI/ErgonomicTransportButtons"), &mErgonomicTransportButtons, true); gPrefs->Read(wxT("/GUI/ErgonomicTransportButtons"), &mErgonomicTransportButtons, true);
@ -102,31 +100,12 @@ ControlToolBar::ControlToolBar()
ControlToolBar::~ControlToolBar() ControlToolBar::~ControlToolBar()
{ {
wxTheApp->Disconnect( wxEVT_KEY_DOWN,
wxKeyEventHandler( ControlToolBar::OnKeyDown ),
NULL,
this );
wxTheApp->Disconnect( wxEVT_KEY_UP,
wxKeyEventHandler( ControlToolBar::OnKeyUp ),
NULL,
this );
} }
void ControlToolBar::Create(wxWindow * parent) void ControlToolBar::Create(wxWindow * parent)
{ {
ToolBar::Create(parent); ToolBar::Create(parent);
wxTheApp->Connect( wxEVT_KEY_DOWN,
wxKeyEventHandler( ControlToolBar::OnKeyDown ),
NULL,
this );
wxTheApp->Connect( wxEVT_KEY_UP,
wxKeyEventHandler( ControlToolBar::OnKeyUp ),
NULL,
this );
} }
// This is a convenience function that allows for button creation in // This is a convenience function that allows for button creation in
@ -148,27 +127,16 @@ AButton *ControlToolBar::MakeButton(teBmps eEnabledUp, teBmps eEnabledDown, teBm
return r; return r;
} }
void ControlToolBar::MakeLoopImage() // static
void ControlToolBar::MakeAlternateImages(AButton &button, int idx,
teBmps eEnabledUp,
teBmps eEnabledDown,
teBmps eDisabled)
{ {
// JKC: See ToolBar::MakeButton() for almost identical code. Condense?? ToolBar::MakeAlternateImages(button, idx,
bmpRecoloredUpLarge, bmpRecoloredDownLarge, bmpRecoloredHiliteLarge,
wxSize Size1( theTheme.ImageSize( bmpRecoloredUpLarge )); eEnabledUp, eEnabledDown, eDisabled,
wxSize Size2( theTheme.ImageSize( bmpLoop )); theTheme.ImageSize( bmpRecoloredUpLarge ));
int xoff = (Size1.GetWidth() - Size2.GetWidth())/2;
int yoff = (Size1.GetHeight() - Size2.GetHeight())/2;
wxImage * up2 = OverlayImage(bmpRecoloredUpLarge, bmpLoop, xoff, yoff);
wxImage * hilite2 = OverlayImage(bmpRecoloredHiliteLarge, bmpLoop, xoff, yoff);
wxImage * down2 = OverlayImage(bmpRecoloredDownLarge, bmpLoop, xoff + 1, yoff + 1);
wxImage * disable2 = OverlayImage(bmpRecoloredUpLarge, bmpLoopDisabled, xoff, yoff);
mPlay->SetAlternateImages(*up2, *hilite2, *down2, *disable2);
delete up2;
delete hilite2;
delete down2;
delete disable2;
} }
void ControlToolBar::Populate() void ControlToolBar::Populate()
@ -180,8 +148,10 @@ void ControlToolBar::Populate()
mPlay = MakeButton( bmpPlay, bmpPlay, bmpPlayDisabled, mPlay = MakeButton( bmpPlay, bmpPlay, bmpPlayDisabled,
ID_PLAY_BUTTON, true, _("Play")); ID_PLAY_BUTTON, true, _("Play"));
MakeAlternateImages(*mPlay, 1, bmpLoop, bmpLoop, bmpLoopDisabled);
MakeLoopImage(); MakeAlternateImages(*mPlay, 2,
bmpCutPreview, bmpCutPreview, bmpCutPreviewDisabled);
mPlay->FollowModifierKeys();
mStop = MakeButton( bmpStop, bmpStop, bmpStopDisabled , mStop = MakeButton( bmpStop, bmpStop, bmpStopDisabled ,
ID_STOP_BUTTON, false, _("Stop")); ID_STOP_BUTTON, false, _("Stop"));
@ -194,6 +164,9 @@ void ControlToolBar::Populate()
mRecord = MakeButton(bmpRecord, bmpRecord, bmpRecordDisabled, mRecord = MakeButton(bmpRecord, bmpRecord, bmpRecordDisabled,
ID_RECORD_BUTTON, true, _("Record")); ID_RECORD_BUTTON, true, _("Record"));
MakeAlternateImages(*mRecord, 1, bmpAppendRecord, bmpAppendRecord,
bmpAppendRecordDisabled);
mRecord->FollowModifierKeys();
#if wxUSE_TOOLTIPS #if wxUSE_TOOLTIPS
RegenerateToolsTooltips(); RegenerateToolsTooltips();
@ -397,7 +370,13 @@ void ControlToolBar::EnableDisableButtons()
} }
} }
mPlay->SetEnabled((!recording) || (tracks && !busy)); const bool enablePlay = (!recording) || (tracks && !busy);
mPlay->SetEnabled(enablePlay);
// Enable and disable the other play button
TranscriptionToolBar *const pttb = p->GetTranscriptionToolBar();
if (pttb)
pttb->SetEnabled(enablePlay);
mRecord->SetEnabled(!busy && !playing); mRecord->SetEnabled(!busy && !playing);
mStop->SetEnabled(busy); mStop->SetEnabled(busy);
@ -406,14 +385,26 @@ void ControlToolBar::EnableDisableButtons()
mPause->SetEnabled(true); mPause->SetEnabled(true);
} }
void ControlToolBar::SetPlay(bool down, bool looped) void ControlToolBar::SetPlay(bool down, bool looped, bool cutPreview)
{ {
AudacityProject *p = GetActiveProject();
TranscriptionToolBar *const pttb =
p ? p->GetTranscriptionToolBar(): 0;
if (down) { if (down) {
mPlay->SetAlternate(looped); mPlay->SetAlternateIdx(cutPreview ? 2 : looped ? 1 : 0);
mPlay->PushDown(); mPlay->PushDown();
} else { if (pttb)
// This disables cursor changes for modifier keys
// in the other play button too
pttb->SetPlaying(true, looped, cutPreview);
}
else {
mPlay->PopUp(); mPlay->PopUp();
mPlay->SetAlternate(false); mPlay->SetAlternateIdx(0);
if (pttb)
// This reenables cursor changes for modifier keys
// in the other play button too
pttb->SetPlaying(false, looped, cutPreview);
} }
EnableDisableButtons(); EnableDisableButtons();
} }
@ -444,13 +435,12 @@ bool ControlToolBar::IsRecordDown()
{ {
return mRecord->IsDown(); return mRecord->IsDown();
} }
void ControlToolBar::PlayPlayRegion(double t0, double t1, void ControlToolBar::PlayPlayRegion(double t0, double t1,
bool looped /* = false */, bool looped /* = false */,
bool cutpreview /* = false */, bool cutpreview /* = false */,
TimeTrack *timetrack /* = NULL */) TimeTrack *timetrack /* = NULL */)
{ {
SetPlay(true, looped); SetPlay(true, looped, cutpreview);
if (gAudioIO->IsBusy()) { if (gAudioIO->IsBusy()) {
SetPlay(false); SetPlay(false);
@ -573,7 +563,7 @@ void ControlToolBar::PlayPlayRegion(double t0, double t1,
#ifdef EXPERIMENTAL_MIDI_OUT #ifdef EXPERIMENTAL_MIDI_OUT
NoteTrackArray(), NoteTrackArray(),
#endif #endif
NULL, p->GetRate(), tcp0, tcp1, p, false, timetrack, p->GetRate(), tcp0, tcp1, p, false,
t0, t1-t0); t0, t1-t0);
} else } else
{ {
@ -677,48 +667,6 @@ void ControlToolBar::OnKeyEvent(wxKeyEvent & event)
event.Skip(); event.Skip();
} }
void ControlToolBar::OnKeyDown(wxKeyEvent & event)
{
event.Skip();
if (event.GetKeyCode() == WXK_SHIFT)
{
// Turn the "Play" button into a "Loop" button
if (!mPlay->IsDown())
mPlay->SetAlternate(true);
mShiftKeyTimer.Start(100);
}
}
void ControlToolBar::OnKeyUp(wxKeyEvent & event)
{
event.Skip();
if (event.GetKeyCode() == WXK_SHIFT)
{
// Turn the "Loop" button into a "Play" button
if (!mPlay->IsDown())
mPlay->SetAlternate(false);
}
}
void ControlToolBar::OnTimer(wxTimerEvent & event)
{
event.Skip();
// bug 307 fix:
// Shift key-up events get swallowed if a command with a Shift in its keyboard
// shortcut opens a dialog, and ControlToolBar::OnKeyUp() doesn't get called.
if (!wxGetKeyState(WXK_SHIFT))
{
wxKeyEvent dummyEvent;
dummyEvent.m_keyCode = WXK_SHIFT;
this->OnKeyUp(dummyEvent);
mShiftKeyTimer.Stop();
}
}
void ControlToolBar::OnPlay(wxCommandEvent & WXUNUSED(evt)) void ControlToolBar::OnPlay(wxCommandEvent & WXUNUSED(evt))
{ {
StopPlaying(); StopPlaying();
@ -736,12 +684,11 @@ void ControlToolBar::OnStop(wxCommandEvent & WXUNUSED(evt))
void ControlToolBar::PlayDefault() void ControlToolBar::PlayDefault()
{ {
if(mPlay->WasControlDown()) // Let control have precedence over shift
PlayCurrentRegion(false, true); /* play with cut preview */ const bool cutPreview = mPlay->WasControlDown();
else if(mPlay->WasShiftDown()) const bool looped = !cutPreview &&
PlayCurrentRegion(true); /* play looped */ mPlay->WasShiftDown();
else PlayCurrentRegion(looped, cutPreview);
PlayCurrentRegion(false); /* play normal */
} }
void ControlToolBar::StopPlaying(bool stopStream /* = true*/) void ControlToolBar::StopPlaying(bool stopStream /* = true*/)

View File

@ -14,8 +14,6 @@
#ifndef __AUDACITY_CONTROL_TOOLBAR__ #ifndef __AUDACITY_CONTROL_TOOLBAR__
#define __AUDACITY_CONTROL_TOOLBAR__ #define __AUDACITY_CONTROL_TOOLBAR__
#include <wx/timer.h>
#include "ToolBar.h" #include "ToolBar.h"
#include "../Theme.h" #include "../Theme.h"
@ -44,10 +42,6 @@ class ControlToolBar:public ToolBar {
void UpdatePrefs(); void UpdatePrefs();
virtual void OnKeyEvent(wxKeyEvent & event); virtual void OnKeyEvent(wxKeyEvent & event);
void OnKeyDown(wxKeyEvent & event);
void OnKeyUp(wxKeyEvent & event);
void OnTimer(wxTimerEvent & event);
// msmeyer: These are public, but it's far better to // msmeyer: These are public, but it's far better to
// call the "real" interface functions like PlayCurrentRegion() and // call the "real" interface functions like PlayCurrentRegion() and
@ -60,7 +54,7 @@ class ControlToolBar:public ToolBar {
void OnPause(wxCommandEvent & evt); void OnPause(wxCommandEvent & evt);
//These allow buttons to be controlled externally: //These allow buttons to be controlled externally:
void SetPlay(bool down, bool looped=false); void SetPlay(bool down, bool looped=false, bool cutPreview = false);
void SetStop(bool down); void SetStop(bool down);
void SetRecord(bool down); void SetRecord(bool down);
@ -94,7 +88,13 @@ class ControlToolBar:public ToolBar {
int id, int id,
bool processdownevents, bool processdownevents,
const wxChar *label); const wxChar *label);
void MakeLoopImage();
static
void MakeAlternateImages(AButton &button, int idx,
teBmps eEnabledUp,
teBmps eEnabledDown,
teBmps eDisabled);
void ArrangeButtons(); void ArrangeButtons();
void SetupCutPreviewTracks(double playStart, double cutStart, void SetupCutPreviewTracks(double playStart, double cutStart,
double cutEnd, double playEnd); double cutEnd, double playEnd);
@ -118,8 +118,6 @@ class ControlToolBar:public ToolBar {
AButton *mStop; AButton *mStop;
AButton *mFF; AButton *mFF;
wxTimer mShiftKeyTimer;
static AudacityProject *mBusyProject; static AudacityProject *mBusyProject;
// Maybe button state values shouldn't be duplicated in this toolbar? // Maybe button state values shouldn't be duplicated in this toolbar?

View File

@ -503,22 +503,40 @@ AButton * ToolBar::MakeButton(teBmps eUp,
int xoff = (size.GetWidth() - theTheme.Image(eStandardUp).GetWidth())/2; int xoff = (size.GetWidth() - theTheme.Image(eStandardUp).GetWidth())/2;
int yoff = (size.GetHeight() - theTheme.Image(eStandardUp).GetHeight())/2; int yoff = (size.GetHeight() - theTheme.Image(eStandardUp).GetHeight())/2;
wxImage * up2 = OverlayImage(eUp, eStandardUp, xoff, yoff); typedef std::auto_ptr<wxImage> wxImagePtr;
wxImage * hilite2 = OverlayImage(eHilite, eStandardUp, xoff, yoff); wxImagePtr up2 (OverlayImage(eUp, eStandardUp, xoff, yoff));
wxImage * down2 = OverlayImage(eDown, eStandardDown, xoff + 1, yoff + 1); wxImagePtr hilite2 (OverlayImage(eHilite, eStandardUp, xoff, yoff));
wxImage * disable2 = OverlayImage(eUp, eDisabled, xoff, yoff); wxImagePtr down2 (OverlayImage(eDown, eStandardDown, xoff + 1, yoff + 1));
wxImagePtr disable2 (OverlayImage(eUp, eDisabled, xoff, yoff));
AButton * button = AButton * button =
new AButton(this, id, placement, size, *up2, *hilite2, *down2, new AButton(this, id, placement, size, *up2, *hilite2, *down2,
*disable2, processdownevents); *disable2, processdownevents);
delete up2;
delete down2;
delete hilite2;
delete disable2;
return button; return button;
} }
//static
void ToolBar::MakeAlternateImages(AButton &button, int idx,
teBmps eUp,
teBmps eDown,
teBmps eHilite,
teBmps eStandardUp,
teBmps eStandardDown,
teBmps eDisabled,
wxSize size)
{
int xoff = (size.GetWidth() - theTheme.Image(eStandardUp).GetWidth())/2;
int yoff = (size.GetHeight() - theTheme.Image(eStandardUp).GetHeight())/2;
typedef std::auto_ptr<wxImage> wxImagePtr;
wxImagePtr up (OverlayImage(eUp, eStandardUp, xoff, yoff));
wxImagePtr hilite (OverlayImage(eHilite, eStandardUp, xoff, yoff));
wxImagePtr down (OverlayImage(eDown, eStandardDown, xoff + 1, yoff + 1));
wxImagePtr disable (OverlayImage(eUp, eDisabled, xoff, yoff));
button.SetAlternateImages(idx, *up, *hilite, *down, *disable);
}
// //
// This changes the state a button (from up to down or vice versa) // This changes the state a button (from up to down or vice versa)

View File

@ -127,6 +127,16 @@ class ToolBar:public wxPanel
bool processdownevents, bool processdownevents,
wxSize size); wxSize size);
static
void MakeAlternateImages(AButton &button, int idx,
teBmps eUp,
teBmps eDown,
teBmps eHilite,
teBmps eStandardUp,
teBmps eStandardDown,
teBmps eDisabled,
wxSize size);
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

@ -162,6 +162,16 @@ AButton *TranscriptionToolBar::AddButton(
return r; return r;
} }
void TranscriptionToolBar::MakeAlternateImages(
teBmps eFore, teBmps eDisabled,
int id, unsigned altIdx)
{
ToolBar::MakeAlternateImages(*mButtons[id], altIdx,
bmpRecoloredUpSmall, bmpRecoloredDownSmall, bmpRecoloredHiliteSmall,
eFore, eFore, eDisabled,
theTheme.ImageSize( bmpRecoloredUpSmall ));
}
void TranscriptionToolBar::Populate() void TranscriptionToolBar::Populate()
{ {
// Very similar to code in ControlToolBar... // Very similar to code in ControlToolBar...
@ -170,6 +180,9 @@ void TranscriptionToolBar::Populate()
AddButton(bmpPlay, bmpPlayDisabled, TTB_PlaySpeed, AddButton(bmpPlay, bmpPlayDisabled, TTB_PlaySpeed,
_("Play at selected speed")); _("Play at selected speed"));
MakeAlternateImages(bmpLoop, bmpLoopDisabled, TTB_PlaySpeed, 1);
MakeAlternateImages(bmpCutPreview, bmpCutPreviewDisabled, TTB_PlaySpeed, 2);
mButtons[TTB_PlaySpeed]->FollowModifierKeys();
//Add a slider that controls the speed of playback. //Add a slider that controls the speed of playback.
const int SliderWidth=100; const int SliderWidth=100;
@ -386,7 +399,8 @@ void TranscriptionToolBar::GetSamples(WaveTrack *t, sampleCount *s0, sampleCount
*slen = ss1 - ss0; *slen = ss1 - ss0;
} }
void TranscriptionToolBar::OnPlaySpeed(wxCommandEvent & WXUNUSED(event)) // Come here from button clicks, or commands
void TranscriptionToolBar::PlayAtSpeed(bool looped, bool cutPreview)
{ {
// Can't do anything without an active project // Can't do anything without an active project
AudacityProject * p = GetActiveProject(); AudacityProject * p = GetActiveProject();
@ -427,12 +441,22 @@ void TranscriptionToolBar::OnPlaySpeed(wxCommandEvent & WXUNUSED(event))
#endif #endif
p->GetControlToolBar()->PlayPlayRegion(playRegionStart, p->GetControlToolBar()->PlayPlayRegion(playRegionStart,
playRegionEnd, playRegionEnd,
false, looped,
false, cutPreview,
mTimeTrack); mTimeTrack);
} }
} }
// Come here from button clicks only
void TranscriptionToolBar::OnPlaySpeed(wxCommandEvent & WXUNUSED(event))
{
// Let control have precedence over shift
const bool cutPreview = mButtons[TTB_PlaySpeed]->WasControlDown();
const bool looped = !cutPreview &&
mButtons[TTB_PlaySpeed]->WasShiftDown();
PlayAtSpeed(looped, cutPreview);
}
void TranscriptionToolBar::OnSpeedSlider(wxCommandEvent& WXUNUSED(event)) void TranscriptionToolBar::OnSpeedSlider(wxCommandEvent& WXUNUSED(event))
{ {
mPlaySpeed = (mPlaySpeedSlider->Get()) * 100; mPlaySpeed = (mPlaySpeedSlider->Get()) * 100;
@ -867,12 +891,6 @@ void TranscriptionToolBar::SetKeyType(wxCommandEvent & WXUNUSED(event))
} }
void TranscriptionToolBar::PlayAtSpeed()
{
wxCommandEvent e;
OnPlaySpeed(e);
}
void TranscriptionToolBar::ShowPlaySpeedDialog() void TranscriptionToolBar::ShowPlaySpeedDialog()
{ {
mPlaySpeedSlider->ShowDialog(); mPlaySpeedSlider->ShowDialog();
@ -881,6 +899,24 @@ void TranscriptionToolBar::ShowPlaySpeedDialog()
OnSpeedSlider(e); OnSpeedSlider(e);
} }
void TranscriptionToolBar::SetEnabled(bool enabled)
{
mButtons[TTB_PlaySpeed]->SetEnabled(enabled);
}
void TranscriptionToolBar::SetPlaying(bool down, bool looped, bool cutPreview)
{
AButton *const button = mButtons[TTB_PlaySpeed];
if (down) {
button->SetAlternateIdx(cutPreview ? 2 : looped ? 1 : 0);
button->PushDown();
}
else {
button->SetAlternateIdx(0);
button->PopUp();
}
}
void TranscriptionToolBar::AdjustPlaySpeed(float adj) void TranscriptionToolBar::AdjustPlaySpeed(float adj)
{ {
if (adj < 0) { if (adj < 0) {

View File

@ -92,10 +92,13 @@ class TranscriptionToolBar:public ToolBar {
virtual double GetSensitivity(); virtual double GetSensitivity();
virtual void SetKeyType(wxCommandEvent & event); virtual void SetKeyType(wxCommandEvent & event);
void PlayAtSpeed(); void PlayAtSpeed(bool looped, bool cutPreview);
void ShowPlaySpeedDialog(); void ShowPlaySpeedDialog();
void AdjustPlaySpeed(float adj); void AdjustPlaySpeed(float adj);
void SetEnabled(bool enabled);
void SetPlaying(bool down, bool looped, bool cutPreview);
private: private:
void InitializeTranscriptionToolBar(); void InitializeTranscriptionToolBar();
@ -103,6 +106,9 @@ class TranscriptionToolBar:public ToolBar {
teBmps eFore, teBmps eDisabled, teBmps eFore, teBmps eDisabled,
int id, int id,
const wxChar *label); const wxChar *label);
void MakeAlternateImages(
teBmps eFore, teBmps eDisabled,
int id, unsigned altIdx);
void GetSamples(WaveTrack *t, sampleCount *s0, sampleCount *slen); void GetSamples(WaveTrack *t, sampleCount *s0, sampleCount *slen);
void SetButton(bool newstate, AButton *button); void SetButton(bool newstate, AButton *button);
void RegenerateTooltips(); void RegenerateTooltips();

View File

@ -46,6 +46,110 @@ BEGIN_EVENT_TABLE(AButton, wxWindow)
EVT_ERASE_BACKGROUND(AButton::OnErase) EVT_ERASE_BACKGROUND(AButton::OnErase)
END_EVENT_TABLE() END_EVENT_TABLE()
class AButton::Listener
: public wxEvtHandler
{
public:
Listener (AButton *button);
~Listener();
void OnKeyDown(wxKeyEvent & event);
void OnKeyUp(wxKeyEvent & event);
void OnTimer(wxTimerEvent & event);
DECLARE_CLASS(AButton::Listener);
DECLARE_EVENT_TABLE();
private:
AButton *mButton;
wxTimer mShiftKeyTimer;
};
IMPLEMENT_CLASS(AButton::Listener, wxEvtHandler);
BEGIN_EVENT_TABLE(AButton::Listener, wxEvtHandler)
EVT_TIMER(wxID_ANY, AButton::Listener::OnTimer)
END_EVENT_TABLE()
AButton::Listener::Listener (AButton *button)
: mButton(button)
{
mShiftKeyTimer.SetOwner(this);
wxTheApp->Connect( wxEVT_KEY_DOWN,
wxKeyEventHandler( AButton::Listener::OnKeyDown ),
NULL,
this );
wxTheApp->Connect( wxEVT_KEY_UP,
wxKeyEventHandler( AButton::Listener::OnKeyUp ),
NULL,
this );
}
AButton::Listener::~Listener ()
{
wxTheApp->Disconnect( wxEVT_KEY_DOWN,
wxKeyEventHandler( AButton::Listener::OnKeyDown ),
NULL,
this );
wxTheApp->Disconnect( wxEVT_KEY_UP,
wxKeyEventHandler( AButton::Listener::OnKeyUp ),
NULL,
this );
}
void AButton::Listener::OnKeyDown(wxKeyEvent & event)
{
// Really, it's all the same check for changes of key states.
OnKeyUp(event);
// See comments in OnTimer()
mShiftKeyTimer.Start(100);
}
void AButton::Listener::OnKeyUp(wxKeyEvent & event)
{
event.Skip();
if (!mButton->IsDown())
{
int idx = 0;
// Ignore the event, consult key states. One modifier key might
// have gone up but another remained down.
// Note that CMD (or CTRL) takes precedence over Shift if both are down
// and alternates are defined for both
// see also AButton::OnMouseEvent()
if (wxGetKeyState(WXK_CONTROL) && mButton->HasAlternateImages(2))
idx = 2;
else if (wxGetKeyState(WXK_SHIFT) && mButton->HasAlternateImages(1))
idx = 1;
// Turn e.g. the "Play" button into a "Loop" button
// or "Cut Preview" button
mButton->SetAlternateIdx(idx);
}
}
void AButton::Listener::OnTimer(wxTimerEvent & event)
{
event.Skip();
// bug 307 fix:
// Shift key-up events get swallowed if a command with a Shift in its keyboard
// shortcut opens a dialog, and OnKeyUp() doesn't get called.
// With CTRL now causing the button to change appearance, presumably similar
// can happen with that key.
if (!wxGetKeyState(WXK_SHIFT) ||
!wxGetKeyState(WXK_CONTROL))
{
wxKeyEvent dummy;
this->OnKeyUp(dummy);
mShiftKeyTimer.Stop();
}
}
AButton::AButton(wxWindow * parent, AButton::AButton(wxWindow * parent,
wxWindowID id, wxWindowID id,
const wxPoint & pos, const wxPoint & pos,
@ -108,18 +212,19 @@ void AButton::Init(wxWindow * parent,
mToggle = toggle; mToggle = toggle;
mUseDisabledAsDownHiliteImage = false; mUseDisabledAsDownHiliteImage = false;
mImage[0] = up; mImages.resize(1);
mImage[1] = over; mImages[0].mArr[0] = up;
mImage[2] = down; mImages[0].mArr[1] = over;
mImage[3] = dis; mImages[0].mArr[2] = down;
mImages[0].mArr[3] = dis;
mAlternate = false; mAlternateIdx = 0;
mButtonIsFocused = false; mButtonIsFocused = false;
mFocusRect = GetRect().Deflate( 3, 3 ); mFocusRect = GetRect().Deflate( 3, 3 );
SetSizeHints(mImage[0].GetMinSize(), SetSizeHints(mImages[0].mArr[0].GetMinSize(),
mImage[0].GetMaxSize()); mImages[0].mArr[0].GetMaxSize());
#if wxUSE_ACCESSIBILITY #if wxUSE_ACCESSIBILITY
SetName( wxT("") ); SetName( wxT("") );
@ -132,38 +237,50 @@ void AButton::UseDisabledAsDownHiliteImage(bool flag)
mUseDisabledAsDownHiliteImage = flag; mUseDisabledAsDownHiliteImage = flag;
} }
void AButton::SetAlternateImages(wxImage up, void AButton::SetAlternateImages(unsigned idx,
wxImage up,
wxImage over, wxImage over,
wxImage down, wxImage down,
wxImage dis) wxImage dis)
{ {
mAltImage[0] = ImageRoll(up); if (1 + idx > mImages.size())
mAltImage[1] = ImageRoll(over); mImages.resize(1 + idx);
mAltImage[2] = ImageRoll(down); mImages[idx].mArr[0] = ImageRoll(up);
mAltImage[3] = ImageRoll(dis); mImages[idx].mArr[1] = ImageRoll(over);
mImages[idx].mArr[2] = ImageRoll(down);
mImages[idx].mArr[3] = ImageRoll(dis);
} }
void AButton::SetAlternateImages(ImageRoll up, void AButton::SetAlternateImages(unsigned idx,
ImageRoll up,
ImageRoll over, ImageRoll over,
ImageRoll down, ImageRoll down,
ImageRoll dis) ImageRoll dis)
{ {
mAltImage[0] = up; if (1 + idx > mImages.size())
mAltImage[1] = over; mImages.resize(1 + idx);
mAltImage[2] = down; mImages[idx].mArr[0] = up;
mAltImage[3] = dis; mImages[idx].mArr[1] = over;
mImages[idx].mArr[2] = down;
mImages[idx].mArr[3] = dis;
} }
void AButton::SetAlternate(bool useAlternateImages) void AButton::SetAlternateIdx(unsigned idx)
{ {
// If alternate-image-state is already correct then // If alternate-image-state is already correct then
// nothing to do (saves repainting button). // nothing to do (saves repainting button).
if( mAlternate == useAlternateImages ) if( mAlternateIdx == idx )
return; return;
mAlternate = useAlternateImages; mAlternateIdx = idx;
Refresh(false); Refresh(false);
} }
void AButton::FollowModifierKeys()
{
if(!mListener.get())
mListener.reset(new Listener(this));
}
void AButton::SetFocusRect(wxRect & r) void AButton::SetFocusRect(wxRect & r)
{ {
mFocusRect = r; mFocusRect = r;
@ -218,10 +335,7 @@ void AButton::OnPaint(wxPaintEvent & WXUNUSED(event))
AButtonState buttonState = GetState(); AButtonState buttonState = GetState();
if (mAlternate) mImages[mAlternateIdx].mArr[buttonState].Draw(dc, GetClientRect());
mAltImage[buttonState].Draw(dc, GetClientRect());
else
mImage[buttonState].Draw(dc, GetClientRect());
#if defined(__WXMSW__) #if defined(__WXMSW__)
if( mButtonIsFocused ) if( mButtonIsFocused )
@ -242,12 +356,17 @@ void AButton::OnSize(wxSizeEvent & WXUNUSED(event))
Refresh(false); Refresh(false);
} }
bool AButton::HasAlternateImages() bool AButton::HasAlternateImages(unsigned idx)
{ {
return (mAltImage[0].Ok() && if (mImages.size() <= idx)
mAltImage[1].Ok() && return false;
mAltImage[2].Ok() &&
mAltImage[3].Ok()); const ImageArr &images = mImages[idx];
const ImageRoll (&arr)[4] = images.mArr;
return (arr[0].Ok() &&
arr[1].Ok() &&
arr[2].Ok() &&
arr[3].Ok());
} }
void AButton::OnMouseEvent(wxMouseEvent & event) void AButton::OnMouseEvent(wxMouseEvent & event)
@ -264,8 +383,17 @@ 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 (HasAlternateImages() && !mButtonIsDown) if (!mButtonIsDown)
mAlternate = event.ShiftDown(); {
// 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)) {

View File

@ -12,6 +12,8 @@
#ifndef __AUDACITY_BUTTON__ #ifndef __AUDACITY_BUTTON__
#define __AUDACITY_BUTTON__ #define __AUDACITY_BUTTON__
#include <vector>
#if wxUSE_ACCESSIBILITY #if wxUSE_ACCESSIBILITY
#include <wx/access.h> #include <wx/access.h>
#endif #endif
@ -23,9 +25,12 @@
class AButton: public wxWindow { class AButton: public wxWindow {
friend class AButtonAx; friend class AButtonAx;
class Listener;
public: public:
// Construct button, specifying images (button up, highlight, button down,
// and disabled) for the default state
AButton(wxWindow * parent, AButton(wxWindow * parent,
wxWindowID id, wxWindowID id,
const wxPoint & pos, const wxPoint & pos,
@ -36,6 +41,8 @@ class AButton: public wxWindow {
ImageRoll dis, ImageRoll dis,
bool toggle); bool toggle);
// Construct button, specifying images (button up, highlight, button down,
// and disabled) for the default state
AButton(wxWindow * parent, AButton(wxWindow * parent,
wxWindowID id, wxWindowID id,
const wxPoint & pos, const wxPoint & pos,
@ -48,17 +55,30 @@ class AButton: public wxWindow {
virtual ~ AButton(); virtual ~ AButton();
virtual void SetAlternateImages(ImageRoll up, // Associate a set of four images (button up, highlight, button down,
// disabled) with one nondefault state of the button
virtual void SetAlternateImages(unsigned idx,
ImageRoll up,
ImageRoll over, ImageRoll over,
ImageRoll down, ImageRoll down,
ImageRoll dis); ImageRoll dis);
virtual void SetAlternateImages(wxImage up, // Associate a set of four images (button up, highlight, button down,
// disabled) with one nondefault state of the button
virtual void SetAlternateImages(unsigned idx,
wxImage up,
wxImage over, wxImage over,
wxImage down, wxImage down,
wxImage dis); wxImage dis);
virtual void SetAlternate(bool useAlternateImages); // Choose state of the button
virtual void SetAlternateIdx(unsigned idx);
// Make the button change appearance with the modifier keys, no matter
// where the mouse is:
// Use state 2 when CTRL is down, else 1 when SHIFT is down, else 0
virtual void FollowModifierKeys();
virtual void SetFocusRect(wxRect & r); virtual void SetFocusRect(wxRect & r);
virtual bool IsEnabled() const { return mEnabled; } virtual bool IsEnabled() const { return mEnabled; }
@ -103,7 +123,7 @@ class AButton: public wxWindow {
private: private:
bool HasAlternateImages(); bool HasAlternateImages(unsigned idx);
void Init(wxWindow * parent, void Init(wxWindow * parent,
wxWindowID id, wxWindowID id,
@ -115,7 +135,7 @@ class AButton: public wxWindow {
ImageRoll dis, ImageRoll dis,
bool toggle); bool toggle);
bool mAlternate; unsigned mAlternateIdx;
bool mToggle; // This bool, if true, makes the button able to bool mToggle; // This bool, if true, makes the button able to
// process events when it is in the down state, and // process events when it is in the down state, and
// moving to the opposite state when it is clicked. // moving to the opposite state when it is clicked.
@ -133,11 +153,13 @@ class AButton: public wxWindow {
bool mEnabled; bool mEnabled;
bool mUseDisabledAsDownHiliteImage; bool mUseDisabledAsDownHiliteImage;
ImageRoll mImage[4]; struct ImageArr { ImageRoll mArr[4]; };
ImageRoll mAltImage[4]; std::vector<ImageArr> mImages;
wxRect mFocusRect; wxRect mFocusRect;
std::auto_ptr<Listener> mListener;
public: public:
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()