vcl/inc/osx/salnsmenu.h |   14 +++++++-
 vcl/osx/salmenu.cxx     |   14 +++++++-
 vcl/osx/salnsmenu.mm    |   84 ++++++++++++++++++++++++++++++++++++++++++++++++
 vcl/osx/vclnsapp.mm     |   49 ++++++----------------------
 4 files changed, 121 insertions(+), 40 deletions(-)

New commits:
commit 4f907fbe52749265ba4eb7c40ed187b453bb1de9
Author:     Patrick Luby <guibmac...@gmail.com>
AuthorDate: Sat Feb 17 10:33:08 2024 -0500
Commit:     Patrick Luby <guibomac...@gmail.com>
CommitDate: Sun Feb 18 13:38:15 2024 +0100

    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.
    
    Also, disable all menu items when displaying modal windows. For
    some unknown reason, key shortcuts are dispatched to the main
    menu instead of the modal window so enable the "autoenabledItems"
    property instances 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.
    
    Change-Id: I6c06024dd6ed05a4ee83f2b37e29c555b52ef555
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163527
    Tested-by: Jenkins
    Reviewed-by: Patrick Luby <guibomac...@gmail.com>

diff --git a/vcl/inc/osx/salnsmenu.h b/vcl/inc/osx/salnsmenu.h
index 696abca2fc0b..64b65e2c95bd 100644
--- a/vcl/inc/osx/salnsmenu.h
+++ b/vcl/inc/osx/salnsmenu.h
@@ -35,17 +35,29 @@ 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 SalNSMainMenuDelegate : NSObject
+{
+}
+- (id)init;
+- (BOOL)menuHasKeyEquivalent:(NSMenu*)pMenu
+                    forEvent:(NSEvent*)pEvent
+                      target:(id*)pTarget
+                      action:(SEL*)pAction;
 @end
 
 #endif // INCLUDED_VCL_INC_OSX_SALNSMENU_H
diff --git a/vcl/osx/salmenu.cxx b/vcl/osx/salmenu.cxx
index b3d02587f46b..7685a1b9e1bc 100644
--- a/vcl/osx/salmenu.cxx
+++ b/vcl/osx/salmenu.cxx
@@ -113,6 +113,7 @@ const AquaSalMenu* AquaSalMenu::pCurrentMenuBar = nullptr;
 
 // FIXME: currently this is leaked
 static MainMenuSelector* pMainMenuSelector = nil;
+static SalNSMainMenuDelegate* pMainMenuDelegate = nil;
 
 static void initAppMenu()
 {
@@ -134,6 +135,10 @@ 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));
@@ -230,12 +235,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 b2df2da7e5f5..0659e412db1a 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,29 @@ SAL_WNODEPRECATED_DECLARATIONS_POP
 }
 @end
 
+@implementation SalNSMainMenuDelegate
+
+-(id)init
+{
+    return [super init];
+}
+
+-(BOOL)menuHasKeyEquivalent: (NSMenu*)pMenu forEvent: (NSEvent*)pEvent target: 
(id*)pTarget action: (SEL*)pAction
+{
+    assert( pMenu == [NSApp mainMenu] );
+
+    // 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];
+
+    // Always return NO since the target and action are not set
+    return NO;
+}
+
+@end
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/vclnsapp.mm b/vcl/osx/vclnsapp.mm
index 5daf923ce105..cd60cb0b0cec 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;

Reply via email to