vcl/inc/osx/salnsmenu.h |   10 +++++-
 vcl/osx/salmenu.cxx     |   13 ++++++-
 vcl/osx/salnsmenu.mm    |   80 ++++++++++++++++++++++++++++++++++++++++++++++++
 vcl/osx/vclnsapp.mm     |   49 ++++++-----------------------
 4 files changed, 111 insertions(+), 41 deletions(-)

New commits:
commit 0060f63d88425149bb37331eb31d0f90372a2957
Author:     Patrick Luby <guibmac...@gmail.com>
AuthorDate: Mon Feb 19 15:11:56 2024 -0500
Commit:     Adolfo Jayme Barrientos <fit...@ubuntu.com>
CommitDate: Wed Feb 21 07:39:14 2024 +0100

    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 <guibomac...@gmail.com>
    (cherry picked from commit 64ca3756416f0355b2008f39120e68ac42269784)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163618
    Reviewed-by: Adolfo Jayme Barrientos <fit...@ubuntu.com>

diff --git a/vcl/inc/osx/salnsmenu.h b/vcl/inc/osx/salnsmenu.h
index 696abca2fc0b..9e0f9acf68ed 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 b3d02587f46b..6ea16a6588ae 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 b2df2da7e5f5..31627a35ea87 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 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