tdf#126638 dispatch key shortcut events to modal windows

While commit 4f907fbe52749265ba4eb7c40ed187b453bb1de9 fixed
tdf#126638, it caused the "disallowed action" beep.

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>
diff --git a/vcl/inc/osx/salnsmenu.h b/vcl/inc/osx/salnsmenu.h
index 64b65e2..9e0f9ac 100644
--- a/vcl/inc/osx/salnsmenu.h
+++ b/vcl/inc/osx/salnsmenu.h
@@ -50,14 +50,10 @@ class AquaSalMenuItem;
- (BOOL)validateMenuItem:(NSMenuItem*)pMenuItem;
@end

@interface SalNSMainMenuDelegate : NSObject
@interface SalNSMainMenu : NSMenu
{
}
- (id)init;
- (BOOL)menuHasKeyEquivalent:(NSMenu*)pMenu
                    forEvent:(NSEvent*)pEvent
                      target:(id*)pTarget
                      action:(SEL*)pAction;
- (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 7685a1b..6ea16a6 100644
--- a/vcl/osx/salmenu.cxx
+++ b/vcl/osx/salmenu.cxx
@@ -113,7 +113,6 @@ const AquaSalMenu* AquaSalMenu::pCurrentMenuBar = nullptr;

// FIXME: currently this is leaked
static MainMenuSelector* pMainMenuSelector = nil;
static SalNSMainMenuDelegate* pMainMenuDelegate = nil;

static void initAppMenu()
{
@@ -126,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: @""];
@@ -135,10 +136,6 @@ static void initAppMenu()
    [NSApp setMainMenu: pMainMenu];

    pMainMenuSelector = [[MainMenuSelector alloc] init];
    pMainMenuDelegate = [[SalNSMainMenuDelegate alloc] init];

    // tdf#126638 set a special delegate for the main menu
    [pMainMenu setDelegate: reinterpret_cast< id<NSMenuDelegate> >(pMainMenuDelegate)];

    // about
    NSString* pString = CreateNSString(VclResId(SV_STDTEXT_ABOUT));
diff --git a/vcl/osx/salnsmenu.mm b/vcl/osx/salnsmenu.mm
index 0659e412d..31627a3 100644
--- a/vcl/osx/salnsmenu.mm
+++ b/vcl/osx/salnsmenu.mm
@@ -317,27 +317,23 @@ SAL_WNODEPRECATED_DECLARATIONS_POP
}
@end

@implementation SalNSMainMenuDelegate
@implementation SalNSMainMenu

-(id)init
- (BOOL)performKeyEquivalent:(NSEvent*)pEvent
{
    return [super init];
}

-(BOOL)menuHasKeyEquivalent: (NSMenu*)pMenu forEvent: (NSEvent*)pEvent target: (id*)pTarget action: (SEL*)pAction
{
    assert( pMenu == [NSApp mainMenu] );
    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,
    // ignore any key shortcut events so use the same existing special
    // dispatching code that -[VCL_NSApplication sendEvent:] uses to
    // dispatch key shortcuts to non-modal, non-LibreOffice windows.
    if( [NSApp modalWindow] )
        [SalNSMenu dispatchSpecialKeyEquivalents: pEvent];
    // 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];

    // Always return NO since the target and action are not set
    return NO;
    return bRet;
}

@end