1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-15 15:49:36 +02:00

Global keyboard capture handling into new files, better comments...

... and break its compile dependency on CommandManager.h by letting it install
callbacks.

This also removes Objective-C mixed code from CommmandManager.

This also eliminates four inclusions of Project.h!

Capture handler state is also global, not per project, though the
CommandManager's callbacks still do depend on the active project.
This commit is contained in:
Paul Licameli 2019-05-14 13:59:24 -04:00
parent 78a1263163
commit db8c4c5e5a
14 changed files with 501 additions and 433 deletions

View File

@ -1865,7 +1865,7 @@
1707407A0988F1BB008541CC /* libsoundtouch.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libsoundtouch.a; sourceTree = BUILT_PRODUCTS_DIR; };
170740960988F2F7008541CC /* libportmixer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libportmixer.a; sourceTree = BUILT_PRODUCTS_DIR; };
170740D40988F820008541CC /* libvorbis.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libvorbis.a; sourceTree = BUILT_PRODUCTS_DIR; };
174D9026098C78AF00D5909F /* CommandManager.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 30; indentWidth = 3; path = CommandManager.cpp; sourceTree = "<group>"; tabWidth = 3; };
174D9026098C78AF00D5909F /* CommandManager.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 3; lastKnownFileType = sourcecode.cpp.cpp; path = CommandManager.cpp; sourceTree = "<group>"; tabWidth = 3; };
174D9027098C78AF00D5909F /* CommandManager.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 3; lastKnownFileType = sourcecode.c.h; path = CommandManager.h; sourceTree = "<group>"; tabWidth = 3; };
174D902A098C78AF00D5909F /* Keyboard.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 3; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; tabWidth = 3; };
174D902B098C78AF00D5909F /* Keyboard.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 3; lastKnownFileType = sourcecode.c.h; path = Keyboard.h; sourceTree = "<group>"; tabWidth = 3; };

View File

@ -30,12 +30,14 @@
#include "Audacity.h"
#include "CellularPanel.h"
#include <wx/eventfilter.h>
#include <wx/setup.h> // for wxUSE_* macros
#include "Project.h"
#include "KeyboardCapture.h"
#include "UIHandle.h"
#include "TrackPanelMouseEvent.h"
#include "HitTestResult.h"
#include "RefreshCode.h"
#include "TrackPanelCell.h"
// A singleton class that intercepts escape key presses when some cellular
// panel is dragging
@ -888,9 +890,9 @@ void CellularPanel::OnSetFocus(wxFocusEvent &event)
void CellularPanel::OnKillFocus(wxFocusEvent & WXUNUSED(event))
{
if (AudacityProject::HasKeyboardCapture(this))
if (KeyboardCapture::IsHandler(this))
{
AudacityProject::ReleaseKeyboard(this);
KeyboardCapture::Release(this);
}
Refresh( false);
}

View File

@ -0,0 +1,387 @@
/**********************************************************************
Audacity: A Digital Audio Editor
KeyboardCapture.cpp
Paul Licameli split this from Project.cpp
**********************************************************************/
#include "KeyboardCapture.h"
#if defined(__WXMAC__)
#include <AppKit/AppKit.h>
#include <wx/osx/private.h>
#elif defined(__WXGTK__)
#include <gtk/gtk.h>
#endif
#include <wx/eventfilter.h>
#include <wx/weakref.h>
#include <wx/window.h>
#include "AudacityException.h"
////////////////////////////////////////////////////////////
/// Custom events
////////////////////////////////////////////////////////////
DEFINE_EVENT_TYPE(EVT_CAPTURE_KEY);
namespace {
wxWindowRef &sHandler()
{
static wxWindowRef theHandler;
return theHandler;
}
KeyboardCapture::FilterFunction &sPreFilter()
{
static KeyboardCapture::FilterFunction theFilter;
return theFilter;
}
KeyboardCapture::FilterFunction &sPostFilter()
{
static KeyboardCapture::FilterFunction theFilter;
return theFilter;
}
}
namespace KeyboardCapture
{
// Keyboard capture
bool IsHandler(const wxWindow *handler)
{
return GetHandler() == handler;
}
wxWindow *GetHandler()
{
return sHandler();
}
void Capture(wxWindow *handler)
{
sHandler() = handler;
}
void Release(wxWindow *handler)
{
// wxASSERT(sHandler() == handler);
sHandler() = nullptr;
}
FilterFunction SetPreFilter( const FilterFunction &function )
{
auto result = sPreFilter();
sPreFilter() = function;
return result;
}
FilterFunction SetPostFilter( const FilterFunction &function )
{
auto result = sPostFilter();
sPostFilter() = function;
return result;
}
void OnFocus( wxWindow &window, wxFocusEvent &event )
{
if (event.GetEventType() == wxEVT_KILL_FOCUS)
KeyboardCapture::Release( &window );
else
KeyboardCapture::Capture( &window );
window.Refresh( false );
event.Skip();
}
}
// Shared by all projects
static class EventMonitor final : public wxEventFilter
{
public:
EventMonitor()
: wxEventFilter()
{
#if defined(__WXMAC__)
// In wx3, the menu accelerators take precendence over key event processing
// so we won't get wxEVT_CHAR_HOOK events for combinations assigned to menus.
// Since we only support OS X 10.6 or greater, we can use an event monitor
// to capture the key event before it gets to the normal wx3 processing.
// The documentation for addLocalMonitorForEventsMatchingMask implies that
// NSKeyUpMask can't be used in 10.6, but testing shows that it can.
NSEventMask mask = NSKeyDownMask | NSKeyUpMask;
mHandler =
[
NSEvent addLocalMonitorForEventsMatchingMask:mask handler:^NSEvent *(NSEvent *event)
{
WXWidget widget = (WXWidget) [ [event window] firstResponder];
if (widget)
{
wxWidgetCocoaImpl *impl = (wxWidgetCocoaImpl *)
wxWidgetImpl::FindFromWXWidget(widget);
if (impl)
{
mEvent = event;
wxKeyEvent wxevent([event type] == NSKeyDown ? wxEVT_KEY_DOWN : wxEVT_KEY_UP);
impl->SetupKeyEvent(wxevent, event);
NSEvent *result;
if ([event type] == NSKeyDown)
{
wxKeyEvent eventHook(wxEVT_CHAR_HOOK, wxevent);
result = FilterEvent(eventHook) == Event_Processed ? nil : event;
}
else
{
result = FilterEvent(wxevent) == Event_Processed ? nil : event;
}
mEvent = nullptr;
return result;
}
}
return event;
}
];
// Bug1252: must also install this filter with wxWidgets, else
// we don't intercept command keys when focus is in a combo box.
wxEvtHandler::AddFilter(this);
#else
wxEvtHandler::AddFilter(this);
#endif
}
~EventMonitor() override
{
#if defined(__WXMAC__)
wxEvtHandler::RemoveFilter(this);
[NSEvent removeMonitor:mHandler];
#else
wxEvtHandler::RemoveFilter(this);
#endif
}
int FilterEvent(wxEvent& event) override
{
// Unguarded exception propagation may crash the program, at least
// on Mac while in the objective-C closure above
return GuardedCall< int > ( [&] {
// Quickly bail if this isn't something we want.
wxEventType type = event.GetEventType();
if (type != wxEVT_CHAR_HOOK && type != wxEVT_KEY_UP)
{
return Event_Skip;
}
wxKeyEvent key = static_cast<wxKeyEvent &>( event );
if ( !( sPreFilter() && sPreFilter()( key ) ) )
return Event_Skip;
// Make a copy of the event and (possibly) make it look like a key down
// event.
if (type == wxEVT_CHAR_HOOK)
{
key.SetEventType(wxEVT_KEY_DOWN);
}
// Give the capture handler first dibs at the event.
wxWindow *handler = KeyboardCapture::GetHandler();
if (handler && HandleCapture(handler, key))
{
return Event_Processed;
}
if ( sPostFilter() && sPostFilter()( key ) )
return Event_Processed;
// Give it back to WX for normal processing.
return Event_Skip;
}, MakeSimpleGuard( Event_Skip ) );
}
private:
// Returns true if the event was captured and processed
bool HandleCapture(wxWindow *target, const wxKeyEvent & event)
{
if (wxGetTopLevelParent(target) != wxGetTopLevelParent(wxWindow::FindFocus()))
{
return false;
}
wxEvtHandler *handler = target->GetEventHandler();
// We make a copy of the event because the capture handler may modify it.
wxKeyEvent temp = event;
#if defined(__WXGTK__)
// wxGTK uses the control and alt modifiers to represent ALTGR,
// so remove it as it might confuse the capture handlers.
if (temp.GetModifiers() == (wxMOD_CONTROL | wxMOD_ALT))
{
temp.SetControlDown(false);
temp.SetAltDown(false);
}
#endif
// Ask the capture handler if the key down/up event is something it
// might be interested in handling.
wxCommandEvent e(EVT_CAPTURE_KEY);
e.SetEventObject(&temp);
e.StopPropagation();
if (!handler->ProcessEvent(e))
{
return false;
}
// Now, let the handler process the normal key event.
bool keyDown = temp.GetEventType() == wxEVT_KEY_DOWN;
temp.WasProcessed();
temp.StopPropagation();
wxEventProcessInHandlerOnly onlyDown(temp, handler);
bool processed = handler->ProcessEvent(temp);
// Don't go any further if the capture handler didn't process
// the key down event.
if (!processed && keyDown)
{
return false;
}
// At this point the capture handler has either processed a key down event
// or we're dealing with a key up event.
//
// So, only generate the char events for key down events.
if (keyDown)
{
wxString chars = GetUnicodeString(temp);
for (size_t i = 0, cnt = chars.length(); i < cnt; i++)
{
temp = event;
temp.SetEventType(wxEVT_CHAR);
temp.WasProcessed();
temp.StopPropagation();
temp.m_uniChar = chars[i];
wxEventProcessInHandlerOnly onlyChar(temp, handler);
handler->ProcessEvent(temp);
}
}
// We get here for processed key down events or for key up events, whether
// processed or not.
return true;
}
// Convert the key down event to a unicode string.
wxString GetUnicodeString(const wxKeyEvent & event)
{
wxString chars;
#if defined(__WXMSW__)
BYTE ks[256];
GetKeyboardState(ks);
WCHAR ucode[256];
int res = ToUnicode(event.GetRawKeyCode(), 0, ks, ucode, 256, 0);
if (res >= 1)
{
chars.Append(ucode, res);
}
#elif defined(__WXGTK__)
chars.Append((wxChar) gdk_keyval_to_unicode(event.GetRawKeyCode()));
#elif defined(__WXMAC__)
if (!mEvent) {
// TODO: we got here without getting the NSEvent pointer,
// as in the combo box case of bug 1252. We can't compute it!
// This makes a difference only when there is a capture handler.
// It's never the case yet that there is one.
wxASSERT(false);
return chars;
}
NSString *c = [mEvent charactersIgnoringModifiers];
if ([c length] == 1)
{
unichar codepoint = [c characterAtIndex:0];
if ((codepoint >= 0xF700 && codepoint <= 0xF8FF) || codepoint == 0x7F)
{
return chars;
}
}
c = [mEvent characters];
chars = [c UTF8String];
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
CFRelease(currentKeyboard);
if (uchr == NULL)
{
return chars;
}
const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);
if (keyboardLayout == NULL)
{
return chars;
}
const UniCharCount maxStringLength = 255;
UniCharCount actualStringLength = 0;
UniChar unicodeString[maxStringLength];
UInt32 nsflags = [mEvent modifierFlags];
UInt16 modifiers = (nsflags & NSAlphaShiftKeyMask ? alphaLock : 0) |
(nsflags & NSShiftKeyMask ? shiftKey : 0) |
(nsflags & NSControlKeyMask ? controlKey : 0) |
(nsflags & NSAlternateKeyMask ? optionKey : 0) |
(nsflags & NSCommandKeyMask ? cmdKey : 0);
OSStatus status = UCKeyTranslate(keyboardLayout,
[mEvent keyCode],
kUCKeyActionDown,
(modifiers >> 8) & 0xff,
LMGetKbdType(),
0,
&mDeadKeyState,
maxStringLength,
&actualStringLength,
unicodeString);
if (status != noErr)
{
return chars;
}
chars = [ [NSString stringWithCharacters:unicodeString
length:(NSInteger)actualStringLength] UTF8String];
#endif
return chars;
}
private:
#if defined(__WXMAC__)
id mHandler;
NSEvent *mEvent {};
UInt32 mDeadKeyState;
#endif
} monitor;

View File

@ -0,0 +1,61 @@
/**********************************************************************
Audacity: A Digital Audio Editor
KeyboardCapture.h
Paul Licameli split this from Project.h
**********************************************************************/
#ifndef __AUDACITY_KEYBOARD_CAPTURE__
#define __AUDACITY_KEYBOARD_CAPTURE__
#include "Audacity.h"
#include <functional>
#include <wx/event.h>
////////////////////////////////////////////////////////////
/// Custom events
////////////////////////////////////////////////////////////
/// \brief Type of event that may be sent to a window while it is
/// installed as the handler with KeyboardCapture::Capture; if it does not skip
/// the event, it will receive further events of type wxEVT_KEY_DOWN,
/// and then wxEVT_CHAR (if key-down was not skipped; those further events
/// don't actually come from wxWidgets, but are simulated by Audacity, as
/// translations of the EVT_CHAR_HOOK event); or, wxEVT_KEY_UP (really from
/// wxWidgets).
DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_CAPTURE_KEY, -1);
namespace KeyboardCapture
{
bool IsHandler(const wxWindow *handler);
wxWindow *GetHandler();
void Capture(wxWindow *handler);
void Release(wxWindow *handler);
using FilterFunction = std::function< bool( wxKeyEvent& ) >;
/// \brief Install a pre-filter, returning the previously installed one
/// Pre-filter is called before passing the event to the captured window; if it
/// returns false, then skip the event entirely
FilterFunction SetPreFilter( const FilterFunction &function );
/// \brief Install a post-filter, returning the previously installed one
/// Post-filter is called if the captured window skips either the
/// EVT_CAPTURE_KEY or the following wxKEY_DOWN event (but not if
/// it skips only the wxEVT_CHAR or wxEVT_KEY_UP event); it is passed a
/// wxKEY_DOWN or a wxKEY_UP event; if it returns false, then the event is
/// skipped
FilterFunction SetPostFilter( const FilterFunction &function );
/// \brief a function useful to implement a focus event handler
/// The window releases the keyboard if the event is for killing focus,
/// otherwise the window captures the keyboard; then refresh the window
/// and skip the event
void OnFocus( wxWindow &window, wxFocusEvent &event );
}
#endif

View File

@ -35,6 +35,7 @@
#include "NoteTrack.h"
#endif
#include "KeyboardCapture.h"
#include "Prefs.h" // for RTL_WORKAROUND
#include "Project.h"
#include "TrackPanel.h" // for EVT_TRACK_PANEL_TIMER
@ -93,16 +94,7 @@ void MixerTrackSlider::OnMouseEvent(wxMouseEvent &event)
void MixerTrackSlider::OnFocus(wxFocusEvent &event)
{
if (event.GetEventType() == wxEVT_KILL_FOCUS) {
AudacityProject::ReleaseKeyboard(this);
}
else {
AudacityProject::CaptureKeyboard(this);
}
Refresh(false);
event.Skip();
KeyboardCapture::OnFocus( *this, event );
}
void MixerTrackSlider::OnCaptureKey(wxCommandEvent &event)

View File

@ -108,6 +108,7 @@ scroll information. It also has some status flags.
#include "InconsistencyException.h"
#include "MixerBoard.h"
#include "import/Import.h"
#include "KeyboardCapture.h"
#include "LabelTrack.h"
#include "Legacy.h"
#include "LyricsWindow.h"
@ -303,11 +304,6 @@ const int sbarHjump = 30; //STM: This is how far the thumb jumps when the
int AudacityProject::mProjectCounter=0;// global counter.
////////////////////////////////////////////////////////////
/// Custom events
////////////////////////////////////////////////////////////
DEFINE_EVENT_TYPE(EVT_CAPTURE_KEY);
//
// This small template class resembles a try-finally block
//
@ -407,7 +403,10 @@ AUDACITY_DLL_API AudacityProject *GetActiveProject()
void SetActiveProject(AudacityProject * project)
{
gActiveProject = project;
if ( gActiveProject != project ) {
gActiveProject = project;
KeyboardCapture::Capture( nullptr );
}
wxTheApp->SetTopWindow(project);
}
@ -5338,50 +5337,6 @@ void AudacityProject::SetSyncLock(bool flag)
}
}
// Keyboard capture
// static
bool AudacityProject::HasKeyboardCapture(const wxWindow *handler)
{
return GetKeyboardCaptureHandler() == handler;
}
// static
wxWindow *AudacityProject::GetKeyboardCaptureHandler()
{
AudacityProject *project = GetActiveProject();
if (project)
{
return project->mKeyboardCaptureHandler;
}
return NULL;
}
// static
void AudacityProject::CaptureKeyboard(wxWindow *handler)
{
AudacityProject *project = GetActiveProject();
if (project)
{
// wxASSERT(project->mKeyboardCaptureHandler == NULL);
project->mKeyboardCaptureHandler = handler;
}
}
// static
void AudacityProject::ReleaseKeyboard(wxWindow * /* handler */)
{
AudacityProject *project = GetActiveProject();
if (project)
{
// wxASSERT(project->mKeyboardCaptureHandler == handler);
project->mKeyboardCaptureHandler = NULL;
}
return;
}
bool AudacityProject::ExportFromTimerRecording(wxFileName fnFile, int iFormat, int iSubFormat, int iFilterIndex)
{
Exporter e;

View File

@ -127,11 +127,6 @@ enum StatusBarField {
rateStatusBarField = 3
};
////////////////////////////////////////////////////////////
/// Custom events
////////////////////////////////////////////////////////////
DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_CAPTURE_KEY, -1);
// XML handler for <import> tag
class ImportXMLTagHandler final : public XMLTagHandler
{
@ -361,12 +356,6 @@ public:
const CommandManager *GetCommandManager() const
{ return mCommandManager.get(); }
// Keyboard capture
static bool HasKeyboardCapture(const wxWindow *handler);
static wxWindow *GetKeyboardCaptureHandler();
static void CaptureKeyboard(wxWindow *handler);
static void ReleaseKeyboard(wxWindow *handler);
void MayStartMonitoring();
@ -738,9 +727,6 @@ public:
private:
// Keyboard capture
wxWindow *mKeyboardCaptureHandler{};
// See explanation in OnCloseWindow
bool mIsBeingDeleted{ false };

View File

@ -68,6 +68,7 @@ is time to refresh some aspect of the screen.
#include "Experimental.h"
#include "AdornedRulerPanel.h"
#include "KeyboardCapture.h"
#include "Project.h"
#include "TrackPanelMouseEvent.h"
#include "TrackPanelResizeHandle.h"
@ -2294,7 +2295,7 @@ void TrackPanel::SetFocusedTrack( Track *t )
auto cell = mAx->SetFocus( Track::SharedPointer( t ) ).get();
if (cell) {
AudacityProject::CaptureKeyboard(this);
KeyboardCapture::Capture(this);
Refresh( false );
}
}

View File

@ -85,8 +85,6 @@ CommandManager. It holds the callback for one command.
#include "CommandContext.h"
#include <wx/defs.h>
#include <wx/eventfilter.h>
#include <wx/evtloop.h>
#include <wx/hash.h>
#include <wx/intl.h>
#include <wx/log.h>
@ -116,12 +114,6 @@ CommandManager. It holds the callback for one command.
#define COMMAND _("Command")
#if defined(__WXMAC__)
#include <AppKit/AppKit.h>
#include <wx/osx/private.h>
#elif defined(__WXGTK__)
#include <gtk/gtk.h>
#endif
NonKeystrokeInterceptingWindow::~NonKeystrokeInterceptingWindow()
{
@ -156,301 +148,6 @@ SubMenuListEntry::~SubMenuListEntry()
{
}
// Shared by all projects
static class CommandManagerEventMonitor final : public wxEventFilter
{
public:
CommandManagerEventMonitor()
: wxEventFilter()
{
#if defined(__WXMAC__)
// In wx3, the menu accelerators take precendence over key event processing
// so we won't get wxEVT_CHAR_HOOK events for combinations assigned to menus.
// Since we only support OS X 10.6 or greater, we can use an event monitor
// to capture the key event before it gets to the normal wx3 processing.
// The documentation for addLocalMonitorForEventsMatchingMask implies that
// NSKeyUpMask can't be used in 10.6, but testing shows that it can.
NSEventMask mask = NSKeyDownMask | NSKeyUpMask;
mHandler =
[
NSEvent addLocalMonitorForEventsMatchingMask:mask handler:^NSEvent *(NSEvent *event)
{
WXWidget widget = (WXWidget) [ [event window] firstResponder];
if (widget)
{
wxWidgetCocoaImpl *impl = (wxWidgetCocoaImpl *)
wxWidgetImpl::FindFromWXWidget(widget);
if (impl)
{
mEvent = event;
wxKeyEvent wxevent([event type] == NSKeyDown ? wxEVT_KEY_DOWN : wxEVT_KEY_UP);
impl->SetupKeyEvent(wxevent, event);
NSEvent *result;
if ([event type] == NSKeyDown)
{
wxKeyEvent eventHook(wxEVT_CHAR_HOOK, wxevent);
result = FilterEvent(eventHook) == Event_Processed ? nil : event;
}
else
{
result = FilterEvent(wxevent) == Event_Processed ? nil : event;
}
mEvent = nullptr;
return result;
}
}
return event;
}
];
// Bug1252: must also install this filter with wxWidgets, else
// we don't intercept command keys when focus is in a combo box.
wxEvtHandler::AddFilter(this);
#else
wxEvtHandler::AddFilter(this);
#endif
}
virtual ~CommandManagerEventMonitor()
{
#if defined(__WXMAC__)
[NSEvent removeMonitor:mHandler];
#else
wxEvtHandler::RemoveFilter(this);
#endif
}
int FilterEvent(wxEvent& event) override
{
// Unguarded exception propagation may crash the program, at least
// on Mac while in the objective-C closure above
return GuardedCall< int > ( [&] {
// Quickly bail if this isn't something we want.
wxEventType type = event.GetEventType();
if (type != wxEVT_CHAR_HOOK && type != wxEVT_KEY_UP)
{
return Event_Skip;
}
// We must have a project since we will be working with the Command Manager
// and capture handler, both of which are (currently) tied to individual projects.
//
// Shouldn't they be tied to the application instead???
AudacityProject *project = GetActiveProject();
if (!project || !project->IsEnabled())
{
return Event_Skip;
}
// Make a copy of the event and (possibly) make it look like a key down
// event.
wxKeyEvent key = (wxKeyEvent &) event;
if (type == wxEVT_CHAR_HOOK)
{
key.SetEventType(wxEVT_KEY_DOWN);
}
// Give the capture handler first dibs at the event.
wxWindow *handler = project->GetKeyboardCaptureHandler();
if (handler && HandleCapture(handler, key))
{
return Event_Processed;
}
// Capture handler didn't want it, so ask the Command Manager.
CommandManager *manager = project->GetCommandManager();
if (manager && manager->FilterKeyEvent(project, key))
{
return Event_Processed;
}
// Give it back to WX for normal processing.
return Event_Skip;
}, MakeSimpleGuard( Event_Skip ) );
}
private:
// Returns true if the event was captured and processed
bool HandleCapture(wxWindow *target, const wxKeyEvent & event)
{
if (wxGetTopLevelParent(target) != wxGetTopLevelParent(wxWindow::FindFocus()))
{
return false;
}
wxEvtHandler *handler = target->GetEventHandler();
// We make a copy of the event because the capture handler may modify it.
wxKeyEvent temp = event;
#if defined(__WXGTK__)
// wxGTK uses the control and alt modifiers to represent ALTGR,
// so remove it as it might confuse the capture handlers.
if (temp.GetModifiers() == (wxMOD_CONTROL | wxMOD_ALT))
{
temp.SetControlDown(false);
temp.SetAltDown(false);
}
#endif
// Ask the capture handler if the key down/up event is something they it
// might be interested in handling.
wxCommandEvent e(EVT_CAPTURE_KEY);
e.SetEventObject(&temp);
e.StopPropagation();
if (!handler->ProcessEvent(e))
{
return false;
}
// Now, let the handler process the normal key event.
bool keyDown = temp.GetEventType() == wxEVT_KEY_DOWN;
temp.WasProcessed();
temp.StopPropagation();
wxEventProcessInHandlerOnly onlyDown(temp, handler);
bool processed = handler->ProcessEvent(temp);
// Don't go any further if the capture handler didn't process
// the key down event.
if (!processed && keyDown)
{
return false;
}
// At this point the capture handler has either processed a key down event
// or we're dealing with a key up event.
//
// So, only generate the char events for key down events.
if (keyDown)
{
wxString chars = GetUnicodeString(temp);
for (size_t i = 0, cnt = chars.length(); i < cnt; i++)
{
temp = event;
temp.SetEventType(wxEVT_CHAR);
temp.WasProcessed();
temp.StopPropagation();
temp.m_uniChar = chars[i];
wxEventProcessInHandlerOnly onlyChar(temp, handler);
handler->ProcessEvent(temp);
}
}
// We get here for processed key down events or for key up events, whether
// processed or not.
return true;
}
// Convert the key down event to a unicode string.
wxString GetUnicodeString(const wxKeyEvent & event)
{
wxString chars;
#if defined(__WXMSW__)
BYTE ks[256];
GetKeyboardState(ks);
WCHAR ucode[256];
int res = ToUnicode(event.GetRawKeyCode(), 0, ks, ucode, 256, 0);
if (res >= 1)
{
chars.Append(ucode, res);
}
#elif defined(__WXGTK__)
chars.Append((wxChar) gdk_keyval_to_unicode(event.GetRawKeyCode()));
#elif defined(__WXMAC__)
if (!mEvent) {
// TODO: we got here without getting the NSEvent pointer,
// as in the combo box case of bug 1252. We can't compute it!
// This makes a difference only when there is a capture handler.
// It's never the case yet that there is one.
wxASSERT(false);
return chars;
}
NSString *c = [mEvent charactersIgnoringModifiers];
if ([c length] == 1)
{
unichar codepoint = [c characterAtIndex:0];
if ((codepoint >= 0xF700 && codepoint <= 0xF8FF) || codepoint == 0x7F)
{
return chars;
}
}
c = [mEvent characters];
chars = [c UTF8String];
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
CFRelease(currentKeyboard);
if (uchr == NULL)
{
return chars;
}
const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);
if (keyboardLayout == NULL)
{
return chars;
}
const UniCharCount maxStringLength = 255;
UniCharCount actualStringLength = 0;
UniChar unicodeString[maxStringLength];
UInt32 nsflags = [mEvent modifierFlags];
UInt16 modifiers = (nsflags & NSAlphaShiftKeyMask ? alphaLock : 0) |
(nsflags & NSShiftKeyMask ? shiftKey : 0) |
(nsflags & NSControlKeyMask ? controlKey : 0) |
(nsflags & NSAlternateKeyMask ? optionKey : 0) |
(nsflags & NSCommandKeyMask ? cmdKey : 0);
OSStatus status = UCKeyTranslate(keyboardLayout,
[mEvent keyCode],
kUCKeyActionDown,
(modifiers >> 8) & 0xff,
LMGetKbdType(),
0,
&mDeadKeyState,
maxStringLength,
&actualStringLength,
unicodeString);
if (status != noErr)
{
return chars;
}
chars = [ [NSString stringWithCharacters:unicodeString
length:(NSInteger)actualStringLength] UTF8String];
#endif
return chars;
}
private:
#if defined(__WXMAC__)
id mHandler;
NSEvent *mEvent {};
UInt32 mDeadKeyState;
#endif
}monitor;
///
/// Standard Constructor
///
@ -1926,3 +1623,24 @@ void CommandManager::CheckDups()
}
#endif
#include "../KeyboardCapture.h"
static struct InstallHandlers
{
InstallHandlers()
{
KeyboardCapture::SetPreFilter( []( wxKeyEvent & ) {
// We must have a project since we will be working with the
// CommandManager, which is tied to individual projects.
AudacityProject *project = GetActiveProject();
return project && project->IsEnabled();
} );
KeyboardCapture::SetPostFilter( []( wxKeyEvent &key ) {
// Capture handler window didn't want it, so ask the CommandManager.
AudacityProject *project = GetActiveProject();
CommandManager *manager = project->GetCommandManager();
return manager && manager->FilterKeyEvent(project, key);
} );
}
} installHandlers;

View File

@ -40,6 +40,7 @@
#include "../AllThemeResources.h"
#include "../AudioIO.h"
#include "../ImageManipulation.h"
#include "../KeyboardCapture.h"
#include "../Prefs.h"
#include "../Project.h"
#include "../ShuttleGui.h"
@ -188,16 +189,7 @@ void DeviceToolBar::RefillCombos()
void DeviceToolBar::OnFocus(wxFocusEvent &event)
{
if (event.GetEventType() == wxEVT_KILL_FOCUS) {
AudacityProject::ReleaseKeyboard(this);
}
else {
AudacityProject::CaptureKeyboard(this);
}
Refresh(false);
event.Skip();
KeyboardCapture::OnFocus( *this, event );
}
void DeviceToolBar::OnCaptureKey(wxCommandEvent &event)

View File

@ -34,8 +34,8 @@
#include "../AllThemeResources.h"
#include "../AudioIO.h"
#include "../ImageManipulation.h"
#include "../KeyboardCapture.h"
#include "../Prefs.h"
#include "../Project.h"
#include "../widgets/ASlider.h"
#include "../widgets/Grabber.h"
@ -117,16 +117,7 @@ void MixerToolBar::Populate()
//Also from SelectionBar;
void MixerToolBar::OnFocus(wxFocusEvent &event)
{
if (event.GetEventType() == wxEVT_KILL_FOCUS) {
AudacityProject::ReleaseKeyboard(this);
}
else {
AudacityProject::CaptureKeyboard(this);
}
Refresh(false);
event.Skip();
KeyboardCapture::OnFocus( *this, event );
}
void MixerToolBar::OnCaptureKey(wxCommandEvent &event)

View File

@ -29,6 +29,8 @@ with changes in the SelectionBar.
#include "../Audacity.h"
#include "SelectionBar.h"
#include "SelectionBarListener.h"
// For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h>
@ -51,8 +53,8 @@ with changes in the SelectionBar.
#include "../widgets/AButton.h"
#include "../AudioIO.h"
#include "../AColor.h"
#include "../KeyboardCapture.h"
#include "../Prefs.h"
#include "../Project.h"
#include "../Snap.h"
#include "../AllThemeResources.h"
@ -699,15 +701,7 @@ void SelectionBar::UpdateRates()
void SelectionBar::OnFocus(wxFocusEvent &event)
{
if (event.GetEventType() == wxEVT_KILL_FOCUS) {
AudacityProject::ReleaseKeyboard(this);
}
else {
AudacityProject::CaptureKeyboard(this);
}
Refresh(false);
event.Skip();
KeyboardCapture::OnFocus( *this, event );
}
void SelectionBar::OnCaptureKey(wxCommandEvent &event)

View File

@ -36,6 +36,7 @@
#include "../AllThemeResources.h"
#include "../AudioIO.h"
#include "../ImageManipulation.h"
#include "../KeyboardCapture.h"
#include "../Project.h"
#include "../TimeTrack.h"
#include "../WaveTrack.h"
@ -337,16 +338,7 @@ void TranscriptionToolBar::RegenerateTooltips()
void TranscriptionToolBar::OnFocus(wxFocusEvent &event)
{
if (event.GetEventType() == wxEVT_KILL_FOCUS) {
AudacityProject::ReleaseKeyboard(this);
}
else {
AudacityProject::CaptureKeyboard(this);
}
Refresh(false);
event.Skip();
KeyboardCapture::OnFocus( *this, event );
}
void TranscriptionToolBar::OnCaptureKey(wxCommandEvent &event)

View File

@ -171,7 +171,7 @@ different formats.
#include "audacity/Types.h"
#include "../AllThemeResources.h"
#include "../AColor.h"
#include "../Project.h"
#include "../KeyboardCapture.h"
#include "../TranslatableStringArray.h"
#include <algorithm>
@ -1707,16 +1707,13 @@ void NumericTextCtrl::OnMouse(wxMouseEvent &event)
void NumericTextCtrl::OnFocus(wxFocusEvent &event)
{
if (event.GetEventType() == wxEVT_KILL_FOCUS) {
AudacityProject::ReleaseKeyboard(this);
}
else {
AudacityProject::CaptureKeyboard(this);
if( mFocusedDigit <=0 )
UpdateAutoFocus();
}
KeyboardCapture::OnFocus( *this, event );
Refresh(false);
if (event.GetEventType() != wxEVT_KILL_FOCUS &&
mFocusedDigit <= 0 )
UpdateAutoFocus();
event.Skip( false ); // PRL: not sure why, but preserving old behavior
}
void NumericTextCtrl::OnCaptureKey(wxCommandEvent &event)