1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-15 23:59:37 +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( bmpRecord, wxImage( 16, 16 ), wxT("Record"));
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 );
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("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("PlaySpeedInc"), _("Increase playback speed"), FN(OnPlaySpeedInc));
c->AddCommand(wxT("PlaySpeedDec"), _("Decrease playback speed"), FN(OnPlaySpeedDec));
@ -3004,7 +3006,23 @@ void AudacityProject::OnPlayAtSpeed()
{
TranscriptionToolBar *tb = GetTranscriptionToolBar();
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
void OnPlayAtSpeed();
void OnPlayAtSpeedLooped();
void OnPlayAtSpeedCutPreview();
void OnSetPlaySpeed();
void OnPlaySpeedInc();
void OnPlaySpeedDec();

View File

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

View File

@ -63,7 +63,7 @@ and use it for toolbar and window layouts too.
#include "Project.h"
#include "toolbars/ToolBar.h"
#include "toolbars/ControlToolBar.h"
#include "toolbars/ToolManager.h"
#include "ImageManipulation.h"
#include "Theme.h"
#include "Experimental.h"
@ -223,9 +223,11 @@ void Theme::EnsureInitialised()
void Theme::ApplyUpdatedImages()
{
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 "ControlToolBar.h"
#include "TranscriptionToolBar.h"
#include "MeterToolBar.h"
#include "../AColor.h"
@ -72,7 +73,6 @@ AudacityProject *ControlToolBar::mBusyProject = NULL;
BEGIN_EVENT_TABLE(ControlToolBar, ToolBar)
EVT_CHAR(ControlToolBar::OnKeyEvent)
EVT_TIMER(wxID_ANY, ControlToolBar::OnTimer)
EVT_BUTTON(ID_PLAY_BUTTON, ControlToolBar::OnPlay)
EVT_BUTTON(ID_STOP_BUTTON, ControlToolBar::OnStop)
EVT_BUTTON(ID_RECORD_BUTTON, ControlToolBar::OnRecord)
@ -89,8 +89,6 @@ END_EVENT_TABLE()
ControlToolBar::ControlToolBar()
: ToolBar(TransportBarID, _("Transport"), wxT("Control"))
{
mShiftKeyTimer.SetOwner(this);
mPaused = false;
gPrefs->Read(wxT("/GUI/ErgonomicTransportButtons"), &mErgonomicTransportButtons, true);
@ -102,31 +100,12 @@ 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)
{
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
@ -148,27 +127,16 @@ AButton *ControlToolBar::MakeButton(teBmps eEnabledUp, teBmps eEnabledDown, teBm
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??
wxSize Size1( theTheme.ImageSize( bmpRecoloredUpLarge ));
wxSize Size2( theTheme.ImageSize( bmpLoop ));
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;
ToolBar::MakeAlternateImages(button, idx,
bmpRecoloredUpLarge, bmpRecoloredDownLarge, bmpRecoloredHiliteLarge,
eEnabledUp, eEnabledDown, eDisabled,
theTheme.ImageSize( bmpRecoloredUpLarge ));
}
void ControlToolBar::Populate()
@ -180,8 +148,10 @@ void ControlToolBar::Populate()
mPlay = MakeButton( bmpPlay, bmpPlay, bmpPlayDisabled,
ID_PLAY_BUTTON, true, _("Play"));
MakeLoopImage();
MakeAlternateImages(*mPlay, 1, bmpLoop, bmpLoop, bmpLoopDisabled);
MakeAlternateImages(*mPlay, 2,
bmpCutPreview, bmpCutPreview, bmpCutPreviewDisabled);
mPlay->FollowModifierKeys();
mStop = MakeButton( bmpStop, bmpStop, bmpStopDisabled ,
ID_STOP_BUTTON, false, _("Stop"));
@ -194,6 +164,9 @@ void ControlToolBar::Populate()
mRecord = MakeButton(bmpRecord, bmpRecord, bmpRecordDisabled,
ID_RECORD_BUTTON, true, _("Record"));
MakeAlternateImages(*mRecord, 1, bmpAppendRecord, bmpAppendRecord,
bmpAppendRecordDisabled);
mRecord->FollowModifierKeys();
#if wxUSE_TOOLTIPS
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);
mStop->SetEnabled(busy);
@ -406,14 +385,26 @@ void ControlToolBar::EnableDisableButtons()
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) {
mPlay->SetAlternate(looped);
mPlay->SetAlternateIdx(cutPreview ? 2 : looped ? 1 : 0);
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->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();
}
@ -444,13 +435,12 @@ bool ControlToolBar::IsRecordDown()
{
return mRecord->IsDown();
}
void ControlToolBar::PlayPlayRegion(double t0, double t1,
bool looped /* = false */,
bool cutpreview /* = false */,
TimeTrack *timetrack /* = NULL */)
{
SetPlay(true, looped);
SetPlay(true, looped, cutpreview);
if (gAudioIO->IsBusy()) {
SetPlay(false);
@ -573,7 +563,7 @@ void ControlToolBar::PlayPlayRegion(double t0, double t1,
#ifdef EXPERIMENTAL_MIDI_OUT
NoteTrackArray(),
#endif
NULL, p->GetRate(), tcp0, tcp1, p, false,
timetrack, p->GetRate(), tcp0, tcp1, p, false,
t0, t1-t0);
} else
{
@ -677,48 +667,6 @@ void ControlToolBar::OnKeyEvent(wxKeyEvent & event)
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))
{
StopPlaying();
@ -736,12 +684,11 @@ void ControlToolBar::OnStop(wxCommandEvent & WXUNUSED(evt))
void ControlToolBar::PlayDefault()
{
if(mPlay->WasControlDown())
PlayCurrentRegion(false, true); /* play with cut preview */
else if(mPlay->WasShiftDown())
PlayCurrentRegion(true); /* play looped */
else
PlayCurrentRegion(false); /* play normal */
// Let control have precedence over shift
const bool cutPreview = mPlay->WasControlDown();
const bool looped = !cutPreview &&
mPlay->WasShiftDown();
PlayCurrentRegion(looped, cutPreview);
}
void ControlToolBar::StopPlaying(bool stopStream /* = true*/)

View File

@ -14,8 +14,6 @@
#ifndef __AUDACITY_CONTROL_TOOLBAR__
#define __AUDACITY_CONTROL_TOOLBAR__
#include <wx/timer.h>
#include "ToolBar.h"
#include "../Theme.h"
@ -44,10 +42,6 @@ class ControlToolBar:public ToolBar {
void UpdatePrefs();
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
// call the "real" interface functions like PlayCurrentRegion() and
@ -60,7 +54,7 @@ class ControlToolBar:public ToolBar {
void OnPause(wxCommandEvent & evt);
//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 SetRecord(bool down);
@ -94,7 +88,13 @@ class ControlToolBar:public ToolBar {
int id,
bool processdownevents,
const wxChar *label);
void MakeLoopImage();
static
void MakeAlternateImages(AButton &button, int idx,
teBmps eEnabledUp,
teBmps eEnabledDown,
teBmps eDisabled);
void ArrangeButtons();
void SetupCutPreviewTracks(double playStart, double cutStart,
double cutEnd, double playEnd);
@ -118,8 +118,6 @@ class ControlToolBar:public ToolBar {
AButton *mStop;
AButton *mFF;
wxTimer mShiftKeyTimer;
static AudacityProject *mBusyProject;
// 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 yoff = (size.GetHeight() - theTheme.Image(eStandardUp).GetHeight())/2;
wxImage * up2 = OverlayImage(eUp, eStandardUp, xoff, yoff);
wxImage * hilite2 = OverlayImage(eHilite, eStandardUp, xoff, yoff);
wxImage * down2 = OverlayImage(eDown, eStandardDown, xoff + 1, yoff + 1);
wxImage * disable2 = OverlayImage(eUp, eDisabled, xoff, yoff);
typedef std::auto_ptr<wxImage> wxImagePtr;
wxImagePtr up2 (OverlayImage(eUp, eStandardUp, xoff, yoff));
wxImagePtr hilite2 (OverlayImage(eHilite, eStandardUp, xoff, yoff));
wxImagePtr down2 (OverlayImage(eDown, eStandardDown, xoff + 1, yoff + 1));
wxImagePtr disable2 (OverlayImage(eUp, eDisabled, xoff, yoff));
AButton * button =
new AButton(this, id, placement, size, *up2, *hilite2, *down2,
*disable2, processdownevents);
delete up2;
delete down2;
delete hilite2;
delete disable2;
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)

View File

@ -127,6 +127,16 @@ class ToolBar:public wxPanel
bool processdownevents,
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 MakeMacRecoloredImage(teBmps eBmpOut, teBmps eBmpIn);

View File

@ -162,6 +162,16 @@ AButton *TranscriptionToolBar::AddButton(
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()
{
// Very similar to code in ControlToolBar...
@ -170,6 +180,9 @@ void TranscriptionToolBar::Populate()
AddButton(bmpPlay, bmpPlayDisabled, TTB_PlaySpeed,
_("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.
const int SliderWidth=100;
@ -386,7 +399,8 @@ void TranscriptionToolBar::GetSamples(WaveTrack *t, sampleCount *s0, sampleCount
*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
AudacityProject * p = GetActiveProject();
@ -427,12 +441,22 @@ void TranscriptionToolBar::OnPlaySpeed(wxCommandEvent & WXUNUSED(event))
#endif
p->GetControlToolBar()->PlayPlayRegion(playRegionStart,
playRegionEnd,
false,
false,
looped,
cutPreview,
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))
{
mPlaySpeed = (mPlaySpeedSlider->Get()) * 100;
@ -867,12 +891,6 @@ void TranscriptionToolBar::SetKeyType(wxCommandEvent & WXUNUSED(event))
}
void TranscriptionToolBar::PlayAtSpeed()
{
wxCommandEvent e;
OnPlaySpeed(e);
}
void TranscriptionToolBar::ShowPlaySpeedDialog()
{
mPlaySpeedSlider->ShowDialog();
@ -881,6 +899,24 @@ void TranscriptionToolBar::ShowPlaySpeedDialog()
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)
{
if (adj < 0) {

View File

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

View File

@ -46,6 +46,110 @@ BEGIN_EVENT_TABLE(AButton, wxWindow)
EVT_ERASE_BACKGROUND(AButton::OnErase)
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,
wxWindowID id,
const wxPoint & pos,
@ -108,18 +212,19 @@ void AButton::Init(wxWindow * parent,
mToggle = toggle;
mUseDisabledAsDownHiliteImage = false;
mImage[0] = up;
mImage[1] = over;
mImage[2] = down;
mImage[3] = dis;
mImages.resize(1);
mImages[0].mArr[0] = up;
mImages[0].mArr[1] = over;
mImages[0].mArr[2] = down;
mImages[0].mArr[3] = dis;
mAlternate = false;
mAlternateIdx = 0;
mButtonIsFocused = false;
mFocusRect = GetRect().Deflate( 3, 3 );
SetSizeHints(mImage[0].GetMinSize(),
mImage[0].GetMaxSize());
SetSizeHints(mImages[0].mArr[0].GetMinSize(),
mImages[0].mArr[0].GetMaxSize());
#if wxUSE_ACCESSIBILITY
SetName( wxT("") );
@ -132,38 +237,50 @@ void AButton::UseDisabledAsDownHiliteImage(bool flag)
mUseDisabledAsDownHiliteImage = flag;
}
void AButton::SetAlternateImages(wxImage up,
void AButton::SetAlternateImages(unsigned idx,
wxImage up,
wxImage over,
wxImage down,
wxImage dis)
{
mAltImage[0] = ImageRoll(up);
mAltImage[1] = ImageRoll(over);
mAltImage[2] = ImageRoll(down);
mAltImage[3] = ImageRoll(dis);
if (1 + idx > mImages.size())
mImages.resize(1 + idx);
mImages[idx].mArr[0] = ImageRoll(up);
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 down,
ImageRoll dis)
{
mAltImage[0] = up;
mAltImage[1] = over;
mAltImage[2] = down;
mAltImage[3] = dis;
if (1 + idx > mImages.size())
mImages.resize(1 + idx);
mImages[idx].mArr[0] = up;
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
// nothing to do (saves repainting button).
if( mAlternate == useAlternateImages )
if( mAlternateIdx == idx )
return;
mAlternate = useAlternateImages;
mAlternateIdx = idx;
Refresh(false);
}
void AButton::FollowModifierKeys()
{
if(!mListener.get())
mListener.reset(new Listener(this));
}
void AButton::SetFocusRect(wxRect & r)
{
mFocusRect = r;
@ -218,10 +335,7 @@ void AButton::OnPaint(wxPaintEvent & WXUNUSED(event))
AButtonState buttonState = GetState();
if (mAlternate)
mAltImage[buttonState].Draw(dc, GetClientRect());
else
mImage[buttonState].Draw(dc, GetClientRect());
mImages[mAlternateIdx].mArr[buttonState].Draw(dc, GetClientRect());
#if defined(__WXMSW__)
if( mButtonIsFocused )
@ -242,12 +356,17 @@ void AButton::OnSize(wxSizeEvent & WXUNUSED(event))
Refresh(false);
}
bool AButton::HasAlternateImages()
bool AButton::HasAlternateImages(unsigned idx)
{
return (mAltImage[0].Ok() &&
mAltImage[1].Ok() &&
mAltImage[2].Ok() &&
mAltImage[3].Ok());
if (mImages.size() <= idx)
return false;
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)
@ -264,8 +383,17 @@ void AButton::OnMouseEvent(wxMouseEvent & event)
(event.m_x >= 0 && event.m_y >= 0 &&
event.m_x < clientSize.x && event.m_y < clientSize.y);
if (HasAlternateImages() && !mButtonIsDown)
mAlternate = event.ShiftDown();
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 (event.ButtonIsDown(wxMOUSE_BTN_ANY)) {

View File

@ -12,6 +12,8 @@
#ifndef __AUDACITY_BUTTON__
#define __AUDACITY_BUTTON__
#include <vector>
#if wxUSE_ACCESSIBILITY
#include <wx/access.h>
#endif
@ -23,9 +25,12 @@
class AButton: public wxWindow {
friend class AButtonAx;
class Listener;
public:
// Construct button, specifying images (button up, highlight, button down,
// and disabled) for the default state
AButton(wxWindow * parent,
wxWindowID id,
const wxPoint & pos,
@ -36,6 +41,8 @@ class AButton: public wxWindow {
ImageRoll dis,
bool toggle);
// Construct button, specifying images (button up, highlight, button down,
// and disabled) for the default state
AButton(wxWindow * parent,
wxWindowID id,
const wxPoint & pos,
@ -48,17 +55,30 @@ class AButton: public wxWindow {
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 down,
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 down,
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 bool IsEnabled() const { return mEnabled; }
@ -103,7 +123,7 @@ class AButton: public wxWindow {
private:
bool HasAlternateImages();
bool HasAlternateImages(unsigned idx);
void Init(wxWindow * parent,
wxWindowID id,
@ -115,7 +135,7 @@ class AButton: public wxWindow {
ImageRoll dis,
bool toggle);
bool mAlternate;
unsigned mAlternateIdx;
bool mToggle; // This bool, if true, makes the button able to
// process events when it is in the down state, and
// moving to the opposite state when it is clicked.
@ -133,11 +153,13 @@ class AButton: public wxWindow {
bool mEnabled;
bool mUseDisabledAsDownHiliteImage;
ImageRoll mImage[4];
ImageRoll mAltImage[4];
struct ImageArr { ImageRoll mArr[4]; };
std::vector<ImageArr> mImages;
wxRect mFocusRect;
std::auto_ptr<Listener> mListener;
public:
DECLARE_EVENT_TABLE()