diff --git a/mac/Audacity.xcodeproj/project.pbxproj b/mac/Audacity.xcodeproj/project.pbxproj index d638d4621..cc540ee6f 100644 --- a/mac/Audacity.xcodeproj/project.pbxproj +++ b/mac/Audacity.xcodeproj/project.pbxproj @@ -2971,6 +2971,7 @@ 28FE4A060ABF4E960056F5C4 /* mmx_optimized.cpp */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 3; lastKnownFileType = sourcecode.cpp.cpp; path = mmx_optimized.cpp; sourceTree = ""; tabWidth = 3; }; 28FE4A070ABF4E960056F5C4 /* sse_optimized.cpp */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 3; lastKnownFileType = sourcecode.cpp.cpp; path = sse_optimized.cpp; sourceTree = ""; tabWidth = 3; }; 28FEC1B21A12B6FB00FACE48 /* EffectAutomationParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EffectAutomationParameters.h; path = ../include/audacity/EffectAutomationParameters.h; sourceTree = SOURCE_ROOT; }; + 5E4685F81CCA9D84008741F2 /* CommandFunctors.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CommandFunctors.h; sourceTree = ""; }; 5E61EE0C1CBAA6BB0009FCF1 /* MemoryX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryX.h; sourceTree = ""; }; 5E74D2D61CC4425D00D88B0B /* TrackPanelOverlay.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TrackPanelOverlay.cpp; sourceTree = ""; }; 5E74D2D71CC4425D00D88B0B /* TrackPanelOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackPanelOverlay.h; sourceTree = ""; }; @@ -3569,58 +3570,59 @@ isa = PBXGroup; children = ( 28D53FFA0FD1912A00FA7C75 /* AppCommandEvent.cpp */, - 28D53FFB0FD1912A00FA7C75 /* AppCommandEvent.h */, 28BD8AA9101DF4C600686679 /* BatchEvalCommand.cpp */, - 28D53FFC0FD1912A00FA7C75 /* BatchEvalCommand.h */, 28851F9C1027F16400152EE1 /* Command.cpp */, - 28D53FFD0FD1912A00FA7C75 /* Command.h */, 28D53FFE0FD1912A00FA7C75 /* CommandBuilder.cpp */, - 28D53FFF0FD1912A00FA7C75 /* CommandBuilder.h */, 28BD8AAA101DF4C600686679 /* CommandDirectory.cpp */, - 28BD8AAB101DF4C600686679 /* CommandDirectory.h */, 28D540000FD1912A00FA7C75 /* CommandHandler.cpp */, - 28D540010FD1912A00FA7C75 /* CommandHandler.h */, 174D9026098C78AF00D5909F /* CommandManager.cpp */, - 174D9027098C78AF00D5909F /* CommandManager.h */, - 1818558F0FFE916C0026D190 /* CommandMisc.h */, 28851F9D1027F16400152EE1 /* CommandSignature.cpp */, - 28851F9E1027F16400152EE1 /* CommandSignature.h */, 28851F9F1027F16400152EE1 /* CommandType.cpp */, - 28851FA01027F16400152EE1 /* CommandType.h */, - 181855900FFE916C0026D190 /* CommandTargets.h */, 28851FA11027F16400152EE1 /* CompareAudioCommand.cpp */, - 28851FA21027F16400152EE1 /* CompareAudioCommand.h */, 28BD8AAC101DF4C600686679 /* ExecMenuCommand.cpp */, - 28BD8AAD101DF4C600686679 /* ExecMenuCommand.h */, 28BD8AAE101DF4C600686679 /* GetAllMenuCommands.cpp */, - 28BD8AAF101DF4C600686679 /* GetAllMenuCommands.h */, 284249EA10D337CE004330A6 /* GetProjectInfoCommand.cpp */, - 284249EB10D337CE004330A6 /* GetProjectInfoCommand.h */, 28851FA31027F16400152EE1 /* GetTrackInfoCommand.cpp */, - 28851FA41027F16400152EE1 /* GetTrackInfoCommand.h */, 28851FA51027F16400152EE1 /* HelpCommand.cpp */, - 28851FA61027F16400152EE1 /* HelpCommand.h */, EDD94ED9103CB520000873F1 /* ImportExportCommands.cpp */, - EDD94EDA103CB520000873F1 /* ImportExportCommands.h */, 174D902A098C78AF00D5909F /* Keyboard.cpp */, - 174D902B098C78AF00D5909F /* Keyboard.h */, 28851FA71027F16400152EE1 /* MessageCommand.cpp */, - 28851FA81027F16400152EE1 /* MessageCommand.h */, EDFCEB9A18894AE600C98E51 /* OpenSaveCommands.cpp */, - EDFCEB9B18894AE600C98E51 /* OpenSaveCommands.h */, 28DE72AA10388583007E18EC /* PreferenceCommands.cpp */, - 28DE72AB10388583007E18EC /* PreferenceCommands.h */, - 28DB34770FDC2C5D0011F589 /* ResponseQueue.h */, 28DB34780FDC2C5D0011F589 /* ResponseQueue.cpp */, 181855950FFE916C0026D190 /* ScreenshotCommand.cpp */, - 181855960FFE916C0026D190 /* ScreenshotCommand.h */, 28D540030FD1912A00FA7C75 /* ScriptCommandRelay.cpp */, - 28D540040FD1912A00FA7C75 /* ScriptCommandRelay.h */, 28851FA91027F16400152EE1 /* SelectCommand.cpp */, - 28851FAA1027F16400152EE1 /* SelectCommand.h */, 284249EC10D337CE004330A6 /* SetProjectInfoCommand.cpp */, - 284249ED10D337CE004330A6 /* SetProjectInfoCommand.h */, 28DE72AC10388583007E18EC /* SetTrackInfoCommand.cpp */, + 28D53FFB0FD1912A00FA7C75 /* AppCommandEvent.h */, + 28D53FFC0FD1912A00FA7C75 /* BatchEvalCommand.h */, + 28D53FFD0FD1912A00FA7C75 /* Command.h */, + 28D53FFF0FD1912A00FA7C75 /* CommandBuilder.h */, + 28BD8AAB101DF4C600686679 /* CommandDirectory.h */, + 5E4685F81CCA9D84008741F2 /* CommandFunctors.h */, + 28D540010FD1912A00FA7C75 /* CommandHandler.h */, + 174D9027098C78AF00D5909F /* CommandManager.h */, + 1818558F0FFE916C0026D190 /* CommandMisc.h */, + 28851F9E1027F16400152EE1 /* CommandSignature.h */, + 181855900FFE916C0026D190 /* CommandTargets.h */, + 28851FA01027F16400152EE1 /* CommandType.h */, + 28851FA21027F16400152EE1 /* CompareAudioCommand.h */, + 28BD8AAD101DF4C600686679 /* ExecMenuCommand.h */, + 28BD8AAF101DF4C600686679 /* GetAllMenuCommands.h */, + 284249EB10D337CE004330A6 /* GetProjectInfoCommand.h */, + 28851FA41027F16400152EE1 /* GetTrackInfoCommand.h */, + 28851FA61027F16400152EE1 /* HelpCommand.h */, + EDD94EDA103CB520000873F1 /* ImportExportCommands.h */, + 174D902B098C78AF00D5909F /* Keyboard.h */, + 28851FA81027F16400152EE1 /* MessageCommand.h */, + EDFCEB9B18894AE600C98E51 /* OpenSaveCommands.h */, + 28DE72AB10388583007E18EC /* PreferenceCommands.h */, + 28DB34770FDC2C5D0011F589 /* ResponseQueue.h */, + 181855960FFE916C0026D190 /* ScreenshotCommand.h */, + 28D540040FD1912A00FA7C75 /* ScriptCommandRelay.h */, + 28851FAA1027F16400152EE1 /* SelectCommand.h */, + 284249ED10D337CE004330A6 /* SetProjectInfoCommand.h */, 28DE72AD10388583007E18EC /* SetTrackInfoCommand.h */, 28BD8AB0101DF4C600686679 /* Validators.h */, ); diff --git a/scripts/mw2html_audacity/mw2html.py b/scripts/mw2html_audacity/mw2html.py index 5f877ec9f..4b2a618b0 100644 --- a/scripts/mw2html_audacity/mw2html.py +++ b/scripts/mw2html_audacity/mw2html.py @@ -528,6 +528,7 @@ def url_open(url): L = urlparse.urlparse(url) if L[1] != domain: conn.close() + if L[1] == '': return(['','']) print "connection to", domain, "closed." conn = httplib.HTTPConnection(L[1]) domain = L[1] diff --git a/src/Makefile.am b/src/Makefile.am index 6fa576381..5df0e7656 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -263,6 +263,7 @@ audacity_SOURCES = \ commands/CommandBuilder.h \ commands/CommandDirectory.cpp \ commands/CommandDirectory.h \ + commands/CommandFunctors.h \ commands/CommandHandler.cpp \ commands/CommandHandler.h \ commands/CommandManager.cpp \ diff --git a/src/Menus.cpp b/src/Menus.cpp index 0d899e84d..f4b6a582f 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -158,76 +158,7 @@ enum { POST_TIMER_RECORD_SHUTDOWN }; -// Define functor subclasses that dispatch to the correct call sequence on -// member functions of AudacityProject - -using audCommandFunction = void (AudacityProject::*)(); -class VoidFunctor final : public CommandFunctor -{ -public: - explicit VoidFunctor(AudacityProject *project, audCommandFunction pfn) - : mProject{ project }, mCommandFunction{ pfn } {} - void operator () (int, const wxEvent *) override - { (mProject->*mCommandFunction) (); } -private: - AudacityProject *const mProject; - const audCommandFunction mCommandFunction; -}; - -using audCommandKeyFunction = void (AudacityProject::*)(const wxEvent *); -class KeyFunctor final : public CommandFunctor -{ -public: - explicit KeyFunctor(AudacityProject *project, audCommandKeyFunction pfn) - : mProject{ project }, mCommandKeyFunction{ pfn } {} - void operator () (int, const wxEvent *evt) override - { (mProject->*mCommandKeyFunction) (evt); } -private: - AudacityProject *const mProject; - const audCommandKeyFunction mCommandKeyFunction; -}; - -using audCommandListFunction = void (AudacityProject::*)(int); -class ListFunctor final : public CommandFunctor -{ -public: - explicit ListFunctor(AudacityProject *project, audCommandListFunction pfn) - : mProject{ project }, mCommandListFunction{ pfn } {} - void operator () (int index, const wxEvent *) override - { (mProject->*mCommandListFunction)(index); } -private: - AudacityProject *const mProject; - const audCommandListFunction mCommandListFunction; -}; - -using audCommandPluginFunction = bool (AudacityProject::*)(const PluginID &, int); -class PluginFunctor final : public CommandFunctor -{ -public: - explicit PluginFunctor(AudacityProject *project, const PluginID &id, audCommandPluginFunction pfn) - : mPluginID{ id }, mProject{ project }, mCommandPluginFunction{ pfn } {} - void operator () (int, const wxEvent *) override - { (mProject->*mCommandPluginFunction) (mPluginID, AudacityProject::OnEffectFlags::kNone); } -private: - const PluginID mPluginID; - AudacityProject *const mProject; - const audCommandPluginFunction mCommandPluginFunction; -}; - -// Now define an overloaded factory function -inline CommandFunctorPointer MakeFunctor(AudacityProject *project, audCommandFunction pfn) -{ return CommandFunctorPointer{ safenew VoidFunctor{ project, pfn } }; } -inline CommandFunctorPointer MakeFunctor(AudacityProject *project, audCommandKeyFunction pfn) -{ return CommandFunctorPointer{ safenew KeyFunctor{ project, pfn } }; } -inline CommandFunctorPointer MakeFunctor(AudacityProject *project, audCommandListFunction pfn) -{ return CommandFunctorPointer{ safenew ListFunctor{ project, pfn } }; } -inline CommandFunctorPointer MakeFunctor(AudacityProject *project, const PluginID &id, audCommandPluginFunction pfn) -{ return CommandFunctorPointer{ safenew PluginFunctor{ project, id, pfn } }; } - -// Now define the macro abbreviations that call the factory -#define FN(X) (MakeFunctor(this, &AudacityProject:: X )) -#define FNS(X, S) (MakeFunctor(this, (S), &AudacityProject:: X )) - +#include "commands/CommandFunctors.h" // // Effects menu arrays // @@ -820,14 +751,7 @@ void AudacityProject::CreateMenusAndCommands() WaveTracksExistFlag | AudioIONotBusyFlag | CanStopAudioStreamFlag); // Scrubbing sub-menu - { - c->BeginSubMenu(_("Scru&bbing")); - c->AddItem(wxT("Scrub"), _("&Scrub"), FN(OnScrub)); - c->AddItem(wxT("ScrollScrub"), _("Sc&rolling Scrub"), FN(OnScrollScrub)); - c->AddItem(wxT("Seek"), _("See&k"), FN(OnSeek)); - c->AddItem(wxT("ScrollSeek"), _("Scrollin&g Seek"), FN(OnScrollSeek)); - c->EndSubMenu(); - } + GetScrubber().AddMenuItems(); c->AddItem(wxT("Pause"), _("&Pause"), FN(OnPause), wxT("P"), c->GetDefaultFlags() | AudioStreamNotScrubbingFlag, @@ -2264,36 +2188,6 @@ void AudacityProject::OnPlayCutPreview() GetControlToolBar()->PlayCurrentRegion(false, true); } -namespace { - inline void DoScrub(AudacityProject *project, bool scroll, bool seek) - { - auto tp = project->GetTrackPanel(); - wxCoord xx = tp->ScreenToClient(::wxGetMouseState().GetPosition()).x; - wxMouseEvent evt; - project->GetScrubber().MarkScrubStart(evt, scroll, seek); - } -} - -void AudacityProject::OnScrub() -{ - DoScrub(this, false, false); -} - -void AudacityProject::OnScrollScrub() -{ - DoScrub(this, true, false); -} - -void AudacityProject::OnSeek() -{ - DoScrub(this, false, true); -} - -void AudacityProject::OnScrollSeek() -{ - DoScrub(this, true, true); -} - void AudacityProject::OnPlayStop() { ControlToolBar *toolbar = GetControlToolBar(); diff --git a/src/Menus.h b/src/Menus.h index e1e6d897b..f8cc32084 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -88,11 +88,6 @@ void OnPlayBeforeAndAfterSelectionEnd(); void OnPlayLooped(); void OnPlayCutPreview(); -void OnScrub(); -void OnScrollScrub(); -void OnSeek(); -void OnScrollSeek(); - // Wave track control void OnTrackPan(); diff --git a/src/Project.cpp b/src/Project.cpp index 316c2c31f..9492224e3 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -285,7 +285,7 @@ public: { } - bool IsSupportedFormat(const wxDataFormat & format, Direction WXUNUSED(dir = Get)) const + bool IsSupportedFormat(const wxDataFormat & format, Direction WXUNUSED(dir = Get)) const override { if (format.GetType() == wxDF_FILENAME) { return true; @@ -318,10 +318,10 @@ public: } #if defined(__WXMAC__) - bool GetData() +#if !wxCHECK_VERSION(3, 0, 0) + bool GetData() override { bool foundSupported = false; -#if !wxCHECK_VERSION(3, 0, 0) bool firstFileAdded = false; OSErr result; @@ -373,11 +373,11 @@ public: break; } } -#endif return foundSupported; } +#endif - bool OnDrop(wxCoord x, wxCoord y) + bool OnDrop(wxCoord x, wxCoord y) override { bool foundSupported = false; #if !wxCHECK_VERSION(3, 0, 0) @@ -411,7 +411,7 @@ public: #endif - bool OnDropFiles(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), const wxArrayString& filenames) + bool OnDropFiles(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), const wxArrayString& filenames) override { //sort by OD non OD. load Non OD first so user can start editing asap. wxArrayString sortednames(filenames); @@ -839,8 +839,6 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, mIsSyncLocked = false; gPrefs->Read(wxT("/GUI/SyncLockTracks"), &mIsSyncLocked, false); - CreateMenusAndCommands(); - // LLL: Read this!!! // // Until the time (and cpu) required to refresh the track panel is @@ -955,6 +953,8 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, mTrackPanel->AddOverlay(mScrubOverlay.get()); #endif + CreateMenusAndCommands(); + // LLL: When Audacity starts or becomes active after returning from // another application, the first window that can accept focus // will be given the focus even if we try to SetFocus(). By diff --git a/src/commands/CommandFunctors.h b/src/commands/CommandFunctors.h index e69de29bb..5df2b27a2 100644 --- a/src/commands/CommandFunctors.h +++ b/src/commands/CommandFunctors.h @@ -0,0 +1,128 @@ +// +// CommandFunctors.h +// Audacity +// +// Created by Paul Licameli on 4/22/16. +// +// + +#ifndef __AUDACITY_COMMAND_FUNCTORS__ +#define __AUDACITY_COMMAND_FUNCTORS__ + +#include +#include "../MemoryX.h" + +class wxEvent; +typedef wxString PluginID; + +class AUDACITY_DLL_API CommandFunctor /* not final */ +{ +public: + CommandFunctor(){}; + virtual ~CommandFunctor(){}; + virtual void operator()(int index, const wxEvent *e) = 0; +}; + +using CommandFunctorPointer = std::shared_ptr ; + + +// Define functor subclasses that dispatch to the correct call sequence on +// member functions of AudacityProject (or other class!) + +template +using audCommandFunction = void (THIS::*)(); + +template +class VoidFunctor final : public CommandFunctor +{ +public: + explicit VoidFunctor(THIS *This, audCommandFunction pfn) + : mThis{ This }, mCommandFunction{ pfn } {} + void operator () (int, const wxEvent *) override + { (mThis->*mCommandFunction) (); } +private: + THIS *const mThis; + const audCommandFunction mCommandFunction; +}; + +template +using audCommandKeyFunction = void (THIS::*)(const wxEvent *); + +template +class KeyFunctor final : public CommandFunctor +{ +public: + explicit KeyFunctor(THIS *This, audCommandKeyFunction pfn) + : mThis{ This }, mCommandKeyFunction{ pfn } {} + void operator () (int, const wxEvent *evt) override + { (mThis->*mCommandKeyFunction) (evt); } +private: + THIS *const mThis; + const audCommandKeyFunction mCommandKeyFunction; +}; + +template +using audCommandListFunction = void (THIS::*)(int); + +template +class ListFunctor final : public CommandFunctor +{ +public: + explicit ListFunctor(THIS *This, audCommandListFunction pfn) + : mThis{ This }, mCommandListFunction{ pfn } {} + void operator () (int index, const wxEvent *) override + { (mThis->*mCommandListFunction)(index); } +private: + THIS *const mThis; + const audCommandListFunction mCommandListFunction; +}; + +template +using audCommandPluginFunction = bool (THIS::*)(const PluginID &, int); + +template +class PluginFunctor final : public CommandFunctor +{ +public: + explicit PluginFunctor(THIS *This, const PluginID &id, audCommandPluginFunction pfn) + : mPluginID{ id }, mThis{ This }, mCommandPluginFunction{ pfn } {} + void operator () (int, const wxEvent *) override + { (mThis->*mCommandPluginFunction) + (mPluginID, + 0 // AudacityProject::OnEffectFlags::kNone + ); } +private: + const PluginID mPluginID; + THIS *const mThis; + const audCommandPluginFunction mCommandPluginFunction; +}; + +// Now define an overloaded factory function +template +inline CommandFunctorPointer MakeFunctor(THIS *This, + audCommandFunction pfn) +{ return CommandFunctorPointer{ safenew VoidFunctor{ This, pfn } }; } + +template +inline CommandFunctorPointer MakeFunctor(THIS *This, + audCommandKeyFunction pfn) +{ return CommandFunctorPointer{ safenew KeyFunctor{ This, pfn } }; } + +template +inline CommandFunctorPointer MakeFunctor(THIS *This, + audCommandListFunction pfn) +{ return CommandFunctorPointer{ safenew ListFunctor{ This, pfn } }; } + +template +inline CommandFunctorPointer MakeFunctor(THIS *This, const PluginID &id, + audCommandPluginFunction pfn) +{ return CommandFunctorPointer{ safenew PluginFunctor{ This, id, pfn } }; } + +// Now define the macro abbreviations that call the factory +#define FNT(THIS, This, X) (MakeFunctor(This, X )) +#define FNTS(THIS, This, X, S) (MakeFunctor(This, (S), X )) + +#define FN(X) FNT(AudacityProject, this, & AudacityProject :: X) +#define FNS(X, S) FNTS(AudacityProject, this, & AudacityProject :: X, S) + +#endif diff --git a/src/commands/CommandManager.h b/src/commands/CommandManager.h index 7b5c83929..80c6a178a 100644 --- a/src/commands/CommandManager.h +++ b/src/commands/CommandManager.h @@ -14,6 +14,8 @@ #include "../Experimental.h" +#include "CommandFunctors.h" + #include "../MemoryX.h" #include #include @@ -26,14 +28,6 @@ #include "audacity/Types.h" -class AUDACITY_DLL_API CommandFunctor /* not final */ -{ -public: - CommandFunctor(){}; - virtual ~CommandFunctor(){}; - virtual void operator()(int index, const wxEvent *e) = 0; -}; - struct MenuBarListEntry { MenuBarListEntry(const wxString &name_, wxMenuBar *menubar_) @@ -54,8 +48,6 @@ struct SubMenuListEntry wxMenu *menu; }; -using CommandFunctorPointer = std::shared_ptr ; - struct CommandListEntry { int id; diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 1737f806d..2a24f0696 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -3192,6 +3192,8 @@ void EffectUIHost::OnPaint(wxPaintEvent & WXUNUSED(evt)) void EffectUIHost::OnClose(wxCloseEvent & WXUNUSED(evt)) { + DoCancel(); + CleanupRealtime(); Hide(); @@ -3261,20 +3263,23 @@ void EffectUIHost::OnApply(wxCommandEvent & evt) return; } +void EffectUIHost::DoCancel() +{ + if (!mCancelled) { + mEffect->mUIResultID = wxID_CANCEL; + + if (IsModal()) + EndModal(false); + else + Hide(); + + mCancelled = true; + } +} + void EffectUIHost::OnCancel(wxCommandEvent & evt) { - mEffect->mUIResultID = evt.GetId(); - - if (IsModal()) - { - EndModal(false); - - Close(); - - return; - } - - Hide(); + DoCancel(); Close(); diff --git a/src/effects/Effect.h b/src/effects/Effect.h index fe33fdba8..5ea0b5bc9 100644 --- a/src/effects/Effect.h +++ b/src/effects/Effect.h @@ -596,6 +596,7 @@ private: void OnPaint(wxPaintEvent & evt); void OnClose(wxCloseEvent & evt); void OnApply(wxCommandEvent & evt); + void DoCancel(); void OnCancel(wxCommandEvent & evt); void OnDebug(wxCommandEvent & evt); void OnMenu(wxCommandEvent & evt); @@ -658,6 +659,8 @@ private: SelectedRegion mRegion; double mPlayPos; + bool mCancelled{}; + DECLARE_EVENT_TABLE(); }; diff --git a/src/toolbars/ToolBar.cpp b/src/toolbars/ToolBar.cpp index 143b80761..cb003a489 100644 --- a/src/toolbars/ToolBar.cpp +++ b/src/toolbars/ToolBar.cpp @@ -67,7 +67,8 @@ public: virtual ~ToolBarResizer(); // We don't need or want to accept focus. - bool AcceptsFocus() const; + // PRL: except for ESC key now. + // bool AcceptsFocus() const; private: void OnErase(wxEraseEvent & event); @@ -77,11 +78,14 @@ private: void OnEnter(wxMouseEvent & event); void OnLeave(wxMouseEvent & event); void OnMotion(wxMouseEvent & event); + void ResizeBar(const wxSize &size); void OnCaptureLost(wxMouseCaptureLostEvent & event); + void OnKeyDown(wxKeyEvent &event); private: ToolBar *mBar; wxPoint mResizeStart; + wxSize mOrigSize; DECLARE_EVENT_TABLE(); }; @@ -96,6 +100,7 @@ BEGIN_EVENT_TABLE( ToolBarResizer, wxWindow ) EVT_LEFT_UP( ToolBarResizer::OnLeftUp ) EVT_MOTION( ToolBarResizer::OnMotion ) EVT_MOUSE_CAPTURE_LOST( ToolBarResizer::OnCaptureLost ) + EVT_KEY_DOWN( ToolBarResizer::OnKeyDown ) END_EVENT_TABLE(); ToolBarResizer::ToolBarResizer(ToolBar *bar) @@ -109,10 +114,12 @@ ToolBarResizer::~ToolBarResizer() { } +/* bool ToolBarResizer::AcceptsFocus() const { return false; } + */ // // Handle background erasure @@ -157,6 +164,8 @@ void ToolBarResizer::OnLeftDown( wxMouseEvent & event ) // Retrieve the mouse position mResizeStart = ClientToScreen( event.GetPosition() ); + mOrigSize = mBar->GetSize(); + // We want all of the mouse events CaptureMouse(); } @@ -181,7 +190,7 @@ void ToolBarResizer::OnMotion( wxMouseEvent & event ) wxPoint raw_pos = event.GetPosition(); wxPoint pos = ClientToScreen( raw_pos ); - if( event.Dragging() ) + if( HasCapture() && event.Dragging() ) { wxRect r = mBar->GetRect(); wxSize msz = mBar->GetMinSize(); @@ -212,18 +221,22 @@ void ToolBarResizer::OnMotion( wxMouseEvent & event ) mResizeStart = pos; } - // Resize the bar - mBar->SetSize( r.GetSize() ); - - // Tell everyone we've changed sizes - mBar->Updated(); - - // Refresh our world - mBar->GetParent()->Refresh(); - mBar->GetParent()->Update(); + ResizeBar( r.GetSize() ); } } +void ToolBarResizer::ResizeBar(const wxSize &size) +{ + mBar->SetSize( size ); + + // Tell everyone we've changed sizes + mBar->Updated(); + + // Refresh our world + mBar->GetParent()->Refresh(); + mBar->GetParent()->Update(); +} + void ToolBarResizer::OnCaptureLost( wxMouseCaptureLostEvent & WXUNUSED(event) ) { if( HasCapture() ) @@ -232,6 +245,15 @@ void ToolBarResizer::OnCaptureLost( wxMouseCaptureLostEvent & WXUNUSED(event) ) } } +void ToolBarResizer::OnKeyDown(wxKeyEvent &event) +{ + event.Skip(); + if (HasCapture() && WXK_ESCAPE == event.GetKeyCode()) { + ResizeBar( mOrigSize ); + ReleaseMouse(); + } +} + //////////////////////////////////////////////////////////// /// Methods for ToolBar //////////////////////////////////////////////////////////// diff --git a/src/toolbars/ToolDock.cpp b/src/toolbars/ToolDock.cpp index b801122d7..5fd488d2a 100644 --- a/src/toolbars/ToolDock.cpp +++ b/src/toolbars/ToolDock.cpp @@ -452,6 +452,11 @@ void ToolDock::Expose( int type, bool show ) Updated(); } +int ToolDock::Find(ToolBar *bar) const +{ + return mDockedBars.Index(bar); +} + // // Queues an EVT_TOOLBAR_UPDATED command event to notify any // interested parties of an updated toolbar or dock layout @@ -468,13 +473,16 @@ void ToolDock::Updated() // void ToolDock::OnGrabber( GrabberEvent & event ) { - ToolBar *t = mBars[ event.GetId() ]; + auto pos = event.GetPosition(); + if (!event.IsEscaping()) { + ToolBar *t = mBars[ event.GetId() ]; - // Pass it on to the manager since it isn't in the handling hierarchy - mManager->ProcessEvent( event ); + // Pass it on to the manager since it isn't in the handling hierarchy + mManager->ProcessEvent( event ); - // We no longer have control - mDockedBars.Remove( t ); + // We no longer have control + mDockedBars.Remove( t ); + } } // diff --git a/src/toolbars/ToolDock.h b/src/toolbars/ToolDock.h index 6816f8203..4d4619a23 100644 --- a/src/toolbars/ToolDock.h +++ b/src/toolbars/ToolDock.h @@ -57,6 +57,7 @@ class ToolDock final : public wxPanel void LayoutToolBars(); void Expose( int type, bool show ); + int Find(ToolBar *bar) const; int GetOrder( ToolBar *bar ); int GetBarCount(); void Dock( ToolBar *bar, int ndx = -1 ); diff --git a/src/toolbars/ToolManager.cpp b/src/toolbars/ToolManager.cpp index 56b837e3b..8cc49b7b2 100644 --- a/src/toolbars/ToolManager.cpp +++ b/src/toolbars/ToolManager.cpp @@ -234,10 +234,7 @@ class ToolFrame final : public wxFrame rect.height = mMinSize.y; } - SetMinSize( rect.GetSize() ); - SetSize( rect.GetSize() ); - Layout(); - Refresh( false ); + Resize( rect.GetSize() ); } else if( HasCapture() && event.LeftUp() ) { @@ -256,6 +253,8 @@ class ToolFrame final : public wxFrame // Is left click within resize grabber? if( r.Contains( pos ) && !event.Leaving() ) { + mOrigSize = GetSize(); + SetCursor( wxCURSOR_SIZENWSE ); if( event.LeftDown() ) { @@ -286,12 +285,30 @@ class ToolFrame final : public wxFrame event.Veto(); } - private: + void OnKeyDown( wxKeyEvent &event ) + { + event.Skip(); + if( HasCapture() && event.GetKeyCode() == WXK_ESCAPE ) { + Resize( mOrigSize ); + ReleaseMouse(); + } + } + + void Resize( const wxSize &size ) + { + SetMinSize( size ); + SetSize( size ); + Layout(); + Refresh( false ); + } + +private: wxWindow *mParent; ToolManager *mManager; ToolBar *mBar; wxSize mMinSize; + wxSize mOrigSize; public: @@ -308,6 +325,7 @@ BEGIN_EVENT_TABLE( ToolFrame, wxFrame ) EVT_MOUSE_CAPTURE_LOST( ToolFrame::OnCaptureLost ) EVT_CLOSE( ToolFrame::OnClose ) EVT_COMMAND( wxID_ANY, EVT_TOOLBAR_UPDATED, ToolFrame::OnToolBarUpdate ) + EVT_KEY_DOWN( ToolFrame::OnKeyDown ) END_EVENT_TABLE() IMPLEMENT_CLASS( ToolManager, wxEvtHandler ); @@ -988,15 +1006,6 @@ void ToolManager::OnMouse( wxMouseEvent & event ) // Button was released...finish the drag if( !event.LeftIsDown() ) { - // Release capture - if( mParent->HasCapture() ) - { - mParent->ReleaseMouse(); - } - - // Hide the indicator - mIndicator->Hide(); - // Transition the bar to a dock if( mDragDock && !event.ShiftDown() ) { @@ -1013,13 +1022,7 @@ void ToolManager::OnMouse( wxMouseEvent & event ) mDragBar->SetDocked( NULL, false ); } - // Done dragging - mDragWindow = NULL; - mDragDock = NULL; - mDragBar = NULL; - mLastPos.x = mBarPos.x = -1; - mLastPos.y = mBarPos.y = -1; - mTimer.Stop(); + DoneDragging(); } else if( event.Dragging() && pos != mLastPos ) { @@ -1221,17 +1224,29 @@ void ToolManager::OnGrabber( GrabberEvent & event ) // No need to propagate any further event.Skip( false ); + if(event.IsEscaping()) + return HandleEscapeKey(); + // Remember which bar we're dragging mDragBar = mBars[ event.GetId() ]; + // Remember state, in case of ESCape key later + if (mDragBar->IsDocked()) { + mPrevDock = dynamic_cast(mDragBar->GetParent()); + wxASSERT(mPrevDock); + mPrevSlot = mPrevDock->Find(mDragBar); + } + else + mPrevPosition = mDragBar->GetParent()->GetPosition(); + // Calculate the drag offset wxPoint mp = event.GetPosition(); mDragOffset = mp - mDragBar->GetParent()->ClientToScreen( mDragBar->GetPosition() ) + - wxPoint( 1, 1 ); + wxPoint( 1, 1 ); // Must set the bar afloat if it's currently docked - if( mDragBar->IsDocked() ) + if( mPrevDock ) { #if defined(__WXMAC__) // Disable window animation @@ -1271,3 +1286,51 @@ void ToolManager::OnGrabber( GrabberEvent & event ) mLastState = wxGetKeyState( WXK_SHIFT ); mTimer.Start( 100 ); } + + +void ToolManager::HandleEscapeKey() +{ + if (mDragBar) { + if(mPrevDock) { + // Sheriff John Stone, + // Why don't you leave me alone? + // Well, I feel so break up + // I want to go home. + mPrevDock->Dock( mDragBar, mPrevSlot ); + + // Done with the floater + mDragWindow->Destroy(); + mDragBar->Refresh(false); + } + else { + // Floater remains, and returns to where it begain + auto parent = mDragBar->GetParent(); + parent->SetPosition(mPrevPosition); + mDragBar->SetDocked(NULL, false); + } + + DoneDragging(); + } +} + +void ToolManager::DoneDragging() +{ + // Done dragging + // Release capture + if( mParent->HasCapture() ) + { + mParent->ReleaseMouse(); + } + + // Hide the indicator + mIndicator->Hide(); + + mDragWindow = NULL; + mDragDock = NULL; + mDragBar = NULL; + mPrevDock = NULL; + mPrevSlot = -1; + mLastPos.x = mBarPos.x = -1; + mLastPos.y = mBarPos.y = -1; + mTimer.Stop(); +} diff --git a/src/toolbars/ToolManager.h b/src/toolbars/ToolManager.h index 25e927a3f..111f8a04f 100644 --- a/src/toolbars/ToolManager.h +++ b/src/toolbars/ToolManager.h @@ -75,6 +75,8 @@ class ToolManager final : public wxEvtHandler void OnMouse( wxMouseEvent & event ); void OnCaptureLost( wxMouseCaptureLostEvent & event ); void OnGrabber( GrabberEvent & event ); + void HandleEscapeKey(); + void DoneDragging(); void OnIndicatorCreate( wxWindowCreateEvent & event ); void OnIndicatorPaint( wxPaintEvent & event ); @@ -87,7 +89,7 @@ class ToolManager final : public wxEvtHandler ToolFrame *mDragWindow; ToolDock *mDragDock; - ToolBar *mDragBar; + ToolBar *mDragBar {}; wxPoint mDragOffset; int mDragBefore; @@ -112,6 +114,10 @@ class ToolManager final : public wxEvtHandler ToolBar *mBars[ ToolBarCount ]; + wxPoint mPrevPosition {}; + ToolDock *mPrevDock {}; + int mPrevSlot {-1}; + public: DECLARE_CLASS( ToolManager ); diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index bf04b9ba2..5610bc413 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -16,6 +16,7 @@ Paul Licameli split from TrackPanel.cpp #include "../../TrackPanel.h" #include "../../TrackPanelCell.h" #include "../../TrackPanelCellIterator.h" +#include "../../commands/CommandFunctors.h" #include "../../toolbars/ControlToolBar.h" #include @@ -134,6 +135,28 @@ Scrubber::~Scrubber() wxActivateEventHandler(Scrubber::OnActivateOrDeactivateApp), NULL, this); } +namespace { + const struct MenuItem { + wxString name; + wxString label; + void (Scrubber::*memFn)(); + bool scroll; + bool seek; + } menuItems[] = { + /* i18n-hint: These commands assist the user in finding a sound by ear. ... + "Scrubbing" is variable-speed playback, ... + "Seeking" is normal speed playback but with skips, ... + "Scrolling" keeps the playback position at a fixed place on screen while the waveform moves + */ + { wxT("Scrub"), XO("&Scrub"), &Scrubber::OnScrub, false, false }, + { wxT("ScrollScrub"), XO("Sc&rolling Scrub"), &Scrubber::OnScrollScrub, true, false }, + { wxT("Seek"), XO("See&k"), &Scrubber::OnSeek, false, true }, + { wxT("ScrollSeek"), XO("Scrollin&g Seek"), &Scrubber::OnScrollSeek, true, true }, + }; + + enum { nMenuItems = sizeof(menuItems) / sizeof(*menuItems) }; +} + void Scrubber::MarkScrubStart( const wxMouseEvent &event #ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL @@ -142,6 +165,8 @@ void Scrubber::MarkScrubStart( , bool alwaysSeeking ) { + UncheckAllMenuItems(); + const wxCoord xx = event.m_x; // Don't actually start scrubbing, but collect some information @@ -158,6 +183,8 @@ void Scrubber::MarkScrubStart( ctb->SetPlay(true, ControlToolBar::PlayAppearance::Scrub); ctb->UpdateStatusBar(mProject); mProject->GetTrackPanel()->HandleCursor(event); + + CheckMenuItem(); } #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT @@ -311,6 +338,8 @@ void Scrubber::ContinueScrubbing() void Scrubber::StopScrubbing() { + UncheckAllMenuItems(); + mScrubStartPosition = -1; mSmoothScrollingScrub = false; const auto ctb = mProject->GetControlToolBar(); @@ -550,4 +579,82 @@ bool Scrubber::PollIsSeeking() return mAlwaysSeeking || ::wxGetMouseState().LeftIsDown(); } +void Scrubber::DoScrub(bool scroll, bool seek) +{ + const bool wasScrubbing = IsScrubbing(); + const bool match = (scroll == mSmoothScrollingScrub && seek == mAlwaysSeeking); + if (!wasScrubbing) { + auto tp = mProject->GetTrackPanel(); + wxCoord xx = tp->ScreenToClient(::wxGetMouseState().GetPosition()).x; + wxMouseEvent evt; + evt.SetX(xx); + MarkScrubStart(evt, scroll, seek); + } + else if(!match) { + mSmoothScrollingScrub = scroll; + mAlwaysSeeking = seek; + UncheckAllMenuItems(); + CheckMenuItem(); + } + else + // unchecks items + StopScrubbing(); +} + +void Scrubber::OnScrub() +{ + DoScrub(false, false); +} + +void Scrubber::OnScrollScrub() +{ + DoScrub(true, false); +} + +void Scrubber::OnSeek() +{ + DoScrub(false, true); +} + +void Scrubber::OnScrollSeek() +{ + DoScrub(true, true); +} + +void Scrubber::AddMenuItems() +{ + auto cm = mProject->GetCommandManager(); + + cm->BeginSubMenu(_("Scru&bbing")); + for (const auto &item : menuItems) { + cm->AddCheck(item.name, wxGetTranslation(item.label), + FNT(Scrubber, this, item.memFn), + false, + WaveTracksExistFlag, WaveTracksExistFlag); + } + cm->EndSubMenu(); + CheckMenuItem(); +} + +void Scrubber::UncheckAllMenuItems() +{ + auto cm = mProject->GetCommandManager(); + for (const auto &item : menuItems) + cm->Check(item.name, false); +} + +void Scrubber::CheckMenuItem() +{ + if(HasStartedScrubbing()) { + auto &item = *std::find_if(menuItems, menuItems + nMenuItems, + [=](const MenuItem &item) { + return mSmoothScrollingScrub == item.scroll && + mAlwaysSeeking == item.seek; + } + ); + auto cm = mProject->GetCommandManager(); + cm->Check(item.name, true); + } +} + #endif diff --git a/src/tracks/ui/Scrubbing.h b/src/tracks/ui/Scrubbing.h index 2edd2cdd0..d4347c691 100644 --- a/src/tracks/ui/Scrubbing.h +++ b/src/tracks/ui/Scrubbing.h @@ -59,8 +59,18 @@ public: void SetSeeking() { mScrubSeekPress = true; } bool PollIsSeeking(); + void AddMenuItems(); + + void OnScrub(); + void OnScrollScrub(); + void OnSeek(); + void OnScrollSeek(); + private: + void DoScrub(bool scroll, bool seek); void OnActivateOrDeactivateApp(wxActivateEvent & event); + void UncheckAllMenuItems(); + void CheckMenuItem(); private: int mScrubToken; diff --git a/src/widgets/Grabber.cpp b/src/widgets/Grabber.cpp index b578b2a98..b0c760c46 100644 --- a/src/widgets/Grabber.cpp +++ b/src/widgets/Grabber.cpp @@ -44,6 +44,7 @@ BEGIN_EVENT_TABLE(Grabber, wxWindow) EVT_LEAVE_WINDOW(Grabber::OnLeave) EVT_LEFT_DOWN(Grabber::OnLeftDown) EVT_PAINT(Grabber::OnPaint) + EVT_KEY_DOWN(Grabber::OnKeyDown) END_EVENT_TABLE() // @@ -78,12 +79,12 @@ Grabber::~Grabber() // // Queue a drag event // -void Grabber::SendEvent(wxEventType type, const wxPoint & pos) +void Grabber::SendEvent(wxEventType type, const wxPoint & pos, bool escaping) { wxWindow *parent = GetParent(); // Initialize event and convert mouse coordinates to screen space - GrabberEvent e(type, GetId(), parent->ClientToScreen(pos)); + GrabberEvent e(type, GetId(), parent->ClientToScreen(pos), escaping); // Set the object of our desire e.SetEventObject(parent); @@ -190,7 +191,7 @@ void Grabber::OnLeftDown(wxMouseEvent & event) PushButton(true); // Notify parent - SendEvent(EVT_GRABBER_CLICKED, event.GetPosition()); + SendEvent(EVT_GRABBER_CLICKED, event.GetPosition(), false); event.Skip(); } @@ -227,3 +228,15 @@ void Grabber::OnPaint(wxPaintEvent & WXUNUSED(event)) // Redraw the grabber DrawGrabber(dc); } + +void Grabber::OnKeyDown(wxKeyEvent &event) +{ + event.Skip(); + + if(event.GetKeyCode() == WXK_ESCAPE) { + // We must not only skip this key event, but propagate it up the window + // hierarchy, so that ToolFrame detects it too. + event.ResumePropagation(wxEVENT_PROPAGATE_MAX); + SendEvent(EVT_GRABBER_CLICKED, wxPoint{ -1, -1 }, true); + } +} diff --git a/src/widgets/Grabber.h b/src/widgets/Grabber.h index 28e1e8f75..e3b4d0417 100644 --- a/src/widgets/Grabber.h +++ b/src/widgets/Grabber.h @@ -45,17 +45,15 @@ class GrabberEvent final : public wxCommandEvent GrabberEvent(wxEventType type = wxEVT_NULL, wxWindowID winid = 0, - const wxPoint& pt = wxDefaultPosition) + const wxPoint& pt = wxDefaultPosition, + bool escaping = false) : wxCommandEvent(type, winid) { mPos = pt; + mEscaping = escaping; } - GrabberEvent(const GrabberEvent & event) - : wxCommandEvent(event) - { - mPos = event.mPos; - } + GrabberEvent(const GrabberEvent & event) = default; // Position of event (in screen coordinates) const wxPoint & GetPosition() const @@ -68,6 +66,8 @@ class GrabberEvent final : public wxCommandEvent mPos = pos; } + bool IsEscaping() const { return mEscaping; } + // Clone is required by wxwidgets; implemented via copy constructor wxEvent *Clone() const override { @@ -77,6 +77,7 @@ class GrabberEvent final : public wxCommandEvent protected: wxPoint mPos; + bool mEscaping {}; }; typedef void (wxEvtHandler::*GrabberEventFunction)(GrabberEvent &); @@ -105,7 +106,8 @@ class Grabber final : public wxWindow // not a need to dock/float a toolbar from the keyboard. If this // changes, remove this and add the necessary keyboard movement // handling. - bool AcceptsFocus() const {return false;} + // PRL: Commented out so the ESC key can stop dragging. + // bool AcceptsFocus() const {return false;} void PushButton(bool state); @@ -115,11 +117,12 @@ class Grabber final : public wxWindow void OnEnter(wxMouseEvent & event); void OnLeave(wxMouseEvent & event); void OnPaint(wxPaintEvent & event); + void OnKeyDown(wxKeyEvent & event); private: void DrawGrabber(wxDC & dc); - void SendEvent(wxEventType type, const wxPoint & pos); + void SendEvent(wxEventType type, const wxPoint & pos, bool escaping); bool mOver; bool mPressed;