tdf#126638 dispatch key shortcut events to modal windows
Some modal windows, such as the native Open and Save dialogs,
return NO from -[NSWindow performKeyEquivalent:]. Fortunately,
the main menu's -[NSMenu performKeyEquivalent:] is then called
so we can catch and redirect any modal window's key shortcut
events without triggering the modal window's "disallowed
action" beep.
Change-Id: Ib1fff68ab159361ceb847881e3a4da4736a33f51
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163605
Tested-by: Jenkins
Reviewed-by: Patrick Luby <guibomacdev@gmail.com>
(cherry picked from commit 64ca3756416f0355b2008f39120e68ac42269784)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163618
Reviewed-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
diff --git a/vcl/inc/osx/salnsmenu.h b/vcl/inc/osx/salnsmenu.h
index 696abca..9e0f9ac 100644
--- a/vcl/inc/osx/salnsmenu.h
+++ b/vcl/inc/osx/salnsmenu.h
@@ -35,17 +35,25 @@ class AquaSalMenuItem;
{
AquaSalMenu* mpMenu;
}
+ (BOOL)dispatchSpecialKeyEquivalents:(NSEvent*)pEvent;
- (id)initWithMenu:(AquaSalMenu*)pMenu;
- (void)menuNeedsUpdate:(NSMenu*)pMenu;
- (void)setSalMenu:(AquaSalMenu*)pMenu;
@end
@interface SalNSMenuItem : NSMenuItem
@interface SalNSMenuItem : NSMenuItem <NSMenuItemValidation>
{
AquaSalMenuItem* mpMenuItem;
}
- (id)initWithMenuItem:(AquaSalMenuItem*)pMenuItem;
- (void)menuItemTriggered:(id)aSender;
- (BOOL)validateMenuItem:(NSMenuItem*)pMenuItem;
@end
@interface SalNSMainMenu : NSMenu
{
}
- (BOOL)performKeyEquivalent:(NSEvent*)pEvent;
@end
#endif // INCLUDED_VCL_INC_OSX_SALNSMENU_H
diff --git a/vcl/osx/salmenu.cxx b/vcl/osx/salmenu.cxx
index b3d02587..6ea16a6 100644
--- a/vcl/osx/salmenu.cxx
+++ b/vcl/osx/salmenu.cxx
@@ -125,7 +125,9 @@ static void initAppMenu()
NSMenu* pAppMenu = nil;
NSMenuItem* pNewItem = nil;
NSMenu* pMainMenu = [[[NSMenu alloc] initWithTitle: @"Main Menu"] autorelease];
// Related: tdf#126638 use NSMenu subclass to catch and redirect key
// shortcuts when a modal window is displayed
SalNSMainMenu* pMainMenu = [[[SalNSMainMenu alloc] initWithTitle: @"Main Menu"] autorelease];
pNewItem = [pMainMenu addItemWithTitle: @"Application"
action: nil
keyEquivalent: @""];
@@ -230,12 +232,19 @@ AquaSalMenu::AquaSalMenu( bool bMenuBar ) :
{
mpMenu = [[SalNSMenu alloc] initWithMenu: this];
[mpMenu setDelegate: reinterpret_cast< id<NSMenuDelegate> >(mpMenu)];
// Related: tdf#126638 enable the menu's "autoenabledItems" property
// Enable the menu's "autoenabledItems" property so that
// -[SalNSMenuItem validateMenuItem:] will be called before handling
// a key shortcut and the menu item can be temporarily disabled if a
// modal window is displayed.
[mpMenu setAutoenablesItems: YES];
}
else
{
mpMenu = [NSApp mainMenu];
[mpMenu setAutoenablesItems: NO];
}
[mpMenu setAutoenablesItems: NO];
}
AquaSalMenu::~AquaSalMenu()
diff --git a/vcl/osx/salnsmenu.mm b/vcl/osx/salnsmenu.mm
index b2df2da..31627a3 100644
--- a/vcl/osx/salnsmenu.mm
+++ b/vcl/osx/salnsmenu.mm
@@ -30,6 +30,53 @@
#include <osx/salnsmenu.h>
@implementation SalNSMenu
+(BOOL)dispatchSpecialKeyEquivalents: (NSEvent*)pEvent
{
if( pEvent && [pEvent type] == NSEventTypeKeyDown )
{
unsigned int nModMask = ([pEvent modifierFlags] & (NSEventModifierFlagShift|NSEventModifierFlagControl|NSEventModifierFlagOption|NSEventModifierFlagCommand));
if( nModMask == NSEventModifierFlagCommand )
{
if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"v"] )
{
if( [NSApp sendAction: @selector(paste:) to: nil from: nil] )
return YES;
}
else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"c"] )
{
if( [NSApp sendAction: @selector(copy:) to: nil from: nil] )
return YES;
}
else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"x"] )
{
if( [NSApp sendAction: @selector(cut:) to: nil from: nil] )
return YES;
}
else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"a"] )
{
if( [NSApp sendAction: @selector(selectAll:) to: nil from: nil] )
return YES;
}
else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"z"] )
{
if( [NSApp sendAction: @selector(undo:) to: nil from: nil] )
return YES;
}
}
else if( nModMask == (NSEventModifierFlagCommand|NSEventModifierFlagShift) )
{
if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"Z"] )
{
if( [NSApp sendAction: @selector(redo:) to: nil from: nil] )
return YES;
}
}
}
return NO;
}
-(id)initWithMenu: (AquaSalMenu*)pMenu
{
mpMenu = pMenu;
@@ -167,6 +214,19 @@
OSL_FAIL( "menubar item without frame !" );
}
}
-(BOOL)validateMenuItem: (NSMenuItem *)pMenuItem
{
// Related: tdf#126638 disable all menu items when displaying modal windows
// For some unknown reason, key shortcuts are dispatched to the LibreOffice
// menu items instead of the modal window so disable all LibreOffice menu
// items while a native modal dialog such as the native Open, Save, or
// Print dialog is displayed.
if (!pMenuItem || [NSApp modalWindow])
return NO;
return [pMenuItem isEnabled];
}
@end
@implementation OOStatusItemView
@@ -257,5 +317,25 @@ SAL_WNODEPRECATED_DECLARATIONS_POP
}
@end
@implementation SalNSMainMenu
- (BOOL)performKeyEquivalent:(NSEvent*)pEvent
{
BOOL bRet = [super performKeyEquivalent: pEvent];
// tdf#126638 dispatch key shortcut events to modal windows
// Some modal windows, such as the native Open and Save dialogs,
// return NO from -[NSWindow performKeyEquivalent:]. Fortunately,
// the main menu's -[NSMenu performKeyEquivalent:] is then called
// so we can catch and redirect any modal window's key shortcut
// events without triggering the modal window's "disallowed
// action" beep.
if( !bRet && [NSApp modalWindow] )
bRet = [SalNSMenu dispatchSpecialKeyEquivalents: pEvent];
return bRet;
}
@end
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/vclnsapp.mm b/vcl/osx/vclnsapp.mm
index 5daf923..cd60cb0 100644
--- a/vcl/osx/vclnsapp.mm
+++ b/vcl/osx/vclnsapp.mm
@@ -34,6 +34,7 @@
#include <osx/salframe.h>
#include <osx/salframeview.h>
#include <osx/salinst.h>
#include <osx/salnsmenu.h>
#include <osx/vclnsapp.h>
#include <quartz/utils.h>
@@ -170,44 +171,8 @@
// precondition: this ONLY works because CMD-V (paste), CMD-C (copy) and CMD-X (cut) are
// NOT localized, that is the same in all locales. Should this be
// different in any locale, this hack will fail.
unsigned int nModMask = ([pEvent modifierFlags] & (NSEventModifierFlagShift|NSEventModifierFlagControl|NSEventModifierFlagOption|NSEventModifierFlagCommand));
if( nModMask == NSEventModifierFlagCommand )
{
if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"v"] )
{
if( [NSApp sendAction: @selector(paste:) to: nil from: nil] )
return;
}
else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"c"] )
{
if( [NSApp sendAction: @selector(copy:) to: nil from: nil] )
return;
}
else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"x"] )
{
if( [NSApp sendAction: @selector(cut:) to: nil from: nil] )
return;
}
else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"a"] )
{
if( [NSApp sendAction: @selector(selectAll:) to: nil from: nil] )
return;
}
else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"z"] )
{
if( [NSApp sendAction: @selector(undo:) to: nil from: nil] )
return;
}
}
else if( nModMask == (NSEventModifierFlagCommand|NSEventModifierFlagShift) )
{
if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"Z"] )
{
if( [NSApp sendAction: @selector(redo:) to: nil from: nil] )
return;
}
}
if( [SalNSMenu dispatchSpecialKeyEquivalents:pEvent] )
return;
}
}
[super sendEvent: pEvent];
@@ -315,6 +280,14 @@
-(NSApplicationTerminateReply)applicationShouldTerminate: (NSApplication *) app
{
(void)app;
// Related: tdf#126638 disable all menu items when displaying modal windows
// Although -[SalNSMenuItem validateMenuItem:] disables almost all menu
// items when a modal window is displayed, the standard Quit menu item
// does not get disabled so disable it here.
if ([NSApp modalWindow])
return NSTerminateCancel;
NSApplicationTerminateReply aReply = NSTerminateNow;
{
SolarMutexGuard aGuard;