tdf#130857 qt weld: Support mail merge "Server Auth" dialog
[LibreOffice.git] / vcl / osx / vclnsapp.mm
blob065e1c95810bff8a622c755c4f3ef7d539973cc5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
20 #include <sal/config.h>
21 #include <config_features.h>
23 #include <vector>
25 #include <stdlib.h>
27 #include <sal/main.h>
28 #include <vcl/commandevent.hxx>
29 #include <vcl/ImageTree.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/window.hxx>
33 #include <osx/saldata.hxx>
34 #include <osx/salframe.h>
35 #include <osx/salframeview.h>
36 #include <osx/salinst.h>
37 #include <osx/salnsmenu.h>
38 #include <osx/vclnsapp.h>
39 #include <quartz/utils.h>
41 #include <premac.h>
42 #include <objc/objc-runtime.h>
43 #import "Carbon/Carbon.h"
44 #import "apple_remote/RemoteControl.h"
45 #include <postmac.h>
48 @implementation CocoaThreadEnabler
49 -(void)enableCocoaThreads:(id)param
51     // do nothing, this is just to start an NSThread and therefore put
52     // Cocoa into multithread mode
53     (void)param;
55 @end
57 // If you wonder how this VCL_NSApplication stuff works, one thing you
58 // might have missed is that the NSPrincipalClass property in
59 // desktop/macosx/Info.plist has the value VCL_NSApplication.
61 @implementation VCL_NSApplication
63 -(void)applicationDidFinishLaunching:(NSNotification*)pNotification
65     (void)pNotification;
67     NSEvent* pEvent = [NSEvent otherEventWithType: NSEventTypeApplicationDefined
68                                location: NSZeroPoint
69                                modifierFlags: 0
70                                timestamp: [[NSProcessInfo processInfo] systemUptime]
71                                windowNumber: 0
72                                context: nil
73                                subtype: AquaSalInstance::AppExecuteSVMain
74                                data1: 0
75                                data2: 0 ];
76     assert( pEvent );
77     [NSApp postEvent: pEvent atStart: NO];
79     if( [NSWindow respondsToSelector:@selector(allowsAutomaticWindowTabbing)] )
80     {
81         [NSWindow setAllowsAutomaticWindowTabbing:NO];
82     }
84     // listen to dark mode change
85     [NSApp addObserver:self forKeyPath:@"effectiveAppearance" options: 0 context: nil];
88 -(void)sendEvent:(NSEvent*)pEvent
90     NSEventType eType = [pEvent type];
91     if( eType == NSEventTypeApplicationDefined )
92     {
93         AquaSalInstance::handleAppDefinedEvent( pEvent );
94     }
95     else if( eType == NSEventTypeKeyDown && ([pEvent modifierFlags] & NSEventModifierFlagCommand) != 0 )
96     {
97         NSWindow* pKeyWin = [NSApp keyWindow];
98         if( pKeyWin && [pKeyWin isKindOfClass: [SalFrameWindow class]] )
99         {
100             // Commit uncommitted text before dispatching key shortcuts. In
101             // certain cases such as pressing Command-Option-C in a Writer
102             // document while there is uncommitted text will call
103             // AquaSalFrame::EndExtTextInput() which will dispatch a
104             // SalEvent::EndExtTextInput event. Writer's handler for that event
105             // will delete the uncommitted text and then insert the committed
106             // text but LibreOffice will crash when deleting the uncommitted
107             // text because deletion of the text also removes and deletes the
108             // newly inserted comment.
109             [static_cast<SalFrameWindow*>(pKeyWin) endExtTextInput];
111             AquaSalFrame* pFrame = [static_cast<SalFrameWindow*>(pKeyWin) getSalFrame];
113             // Related tdf#162010: match against -[NSEvent characters]
114             // When using some non-Western European keyboard layouts, the
115             // event's "characters ignoring modifiers" will be set to the
116             // original Unicode character instead of the resolved key
117             // equivalent character so match against the -[NSEvent characters]
118             // instead.
119             NSEventModifierFlags nModMask = ([pEvent modifierFlags] & (NSEventModifierFlagShift|NSEventModifierFlagControl|NSEventModifierFlagOption|NSEventModifierFlagCommand));
121             // Note: when pressing Command-Option keys, some non-Western
122             // keyboards will set the "characters ignoring modifiers"
123             // property  to the key shortcut character instead of setting
124             // the "characters property. So check for both cases.
125             NSString *pCharacters = [pEvent characters];
126             NSString *pCharactersIgnoringModifiers = [pEvent charactersIgnoringModifiers];
128             /*
129              * #i98949# - Cmd-M miniaturize window, Cmd-Option-M miniaturize all windows
130              */
131             if( [pCharacters isEqualToString: @"m"] || [pCharactersIgnoringModifiers isEqualToString: @"m"] )
132             {
133                 if ( nModMask == NSEventModifierFlagCommand && ([pFrame->getNSWindow() styleMask] & NSWindowStyleMaskMiniaturizable) )
134                 {
135                     [pFrame->getNSWindow() performMiniaturize: nil];
136                     return;
137                 }
138                 else if ( nModMask == ( NSEventModifierFlagCommand | NSEventModifierFlagOption ) )
139                 {
140                     [NSApp miniaturizeAll: nil];
141                     return;
142                 }
143             }
144             // tdf#162190 handle Command-w
145             // On macOS, Command-w should attempt to close the key window.
146             // TODO: Command-Option-w should attempt to close all windows.
147             else if( [pCharacters isEqualToString: @"w"] || [pCharactersIgnoringModifiers isEqualToString: @"w"] )
148             {
149                 if ( nModMask == NSEventModifierFlagCommand && ([pFrame->getNSWindow() styleMask] & NSWindowStyleMaskClosable ) )
150                 {
151                     [pFrame->getNSWindow() performClose: nil];
152                     return;
153                 }
154             }
156             // get information whether the event was handled; keyDown returns nothing
157             GetSalData()->maKeyEventAnswer[ pEvent ] = false;
158             bool bHandled = false;
160             // dispatch to view directly to avoid the key event being consumed by the menubar
161             // popup windows do not get the focus, so they don't get these either
162             // simplest would be dispatch this to the key window always if it is without parent
163             // however e.g. in document we want the menu shortcut if e.g. the stylist has focus
164             if( pFrame->mpParent && !(pFrame->mnStyle & SalFrameStyleFlags::FLOAT) )
165             {
166                 [[pKeyWin contentView] keyDown: pEvent];
167                 bHandled = GetSalData()->maKeyEventAnswer[ pEvent ];
168             }
170             // see whether the main menu consumes this event
171             // if not, we want to dispatch it ourselves. Unless we do this "trick"
172             // the main menu just beeps for an unknown or disabled key equivalent
173             // and swallows the event wholesale
174             NSMenu* pMainMenu = [NSApp mainMenu];
175             if( ! bHandled &&
176                 (pMainMenu == nullptr || ! [NSMenu menuBarVisible] || ! [pMainMenu performKeyEquivalent: pEvent]) )
177             {
178                 [[pKeyWin contentView] keyDown: pEvent];
179                 bHandled = GetSalData()->maKeyEventAnswer[ pEvent ];
180             }
181             else
182             {
183                 bHandled = true;  // event handled already or main menu just handled it
184             }
185             GetSalData()->maKeyEventAnswer.erase( pEvent );
187             if( bHandled )
188                 return;
189         }
190         else if( pKeyWin )
191         {
192             // #i94601# a window not of vcl's making has the focus.
193             // Since our menus do not invoke the usual commands
194             // try to play nice with native windows like the file dialog
195             // and emulate them
196             // precondition: this ONLY works because CMD-V (paste), CMD-C (copy) and CMD-X (cut) are
197             // NOT localized, that is the same in all locales. Should this be
198             // different in any locale, this hack will fail.
199             if( [SalNSMenu dispatchSpecialKeyEquivalents:pEvent] )
200                 return;
201         }
202     }
203     [super sendEvent: pEvent];
206 -(void)sendSuperEvent:(NSEvent*)pEvent
208     [super sendEvent: pEvent];
211 -(NSMenu*)applicationDockMenu:(NSApplication *)sender
213     (void)sender;
214     return AquaSalInstance::GetDynamicDockMenu();
217 -(BOOL)application: (NSApplication*)app openFile: (NSString*)pFile
219     (void)app;
220     std::vector<OUString> aFile { GetOUString( pFile ) };
221     if( ! AquaSalInstance::isOnCommandLine( aFile[0] ) )
222     {
223         const ApplicationEvent* pAppEvent = new ApplicationEvent(ApplicationEvent::Type::Open, std::move(aFile));
224         AquaSalInstance::aAppEventList.push_back( pAppEvent );
225         AquaSalInstance *pInst = GetSalData()->mpInstance;
226         if( pInst )
227             pInst->TriggerUserEventProcessing();
228     }
229     return YES;
232 -(void)application: (NSApplication*) app openFiles: (NSArray*)files
234     (void)app;
235     std::vector<OUString> aFileList;
237     NSEnumerator* it = [files objectEnumerator];
238     NSString* pFile = nil;
240     while( (pFile = [it nextObject]) != nil )
241     {
242         const OUString aFile( GetOUString( pFile ) );
243         if( ! AquaSalInstance::isOnCommandLine( aFile ) )
244         {
245             aFileList.push_back( aFile );
246         }
247     }
249     if( !aFileList.empty() )
250     {
251         // we have no back channel here, we have to assume success, in which case
252         // replyToOpenOrPrint does not need to be called according to documentation
253         // [app replyToOpenOrPrint: NSApplicationDelegateReplySuccess];
254         const ApplicationEvent* pAppEvent = new ApplicationEvent(ApplicationEvent::Type::Open, std::move(aFileList));
255         AquaSalInstance::aAppEventList.push_back( pAppEvent );
256         AquaSalInstance *pInst = GetSalData()->mpInstance;
257         if( pInst )
258             pInst->TriggerUserEventProcessing();
259     }
262 -(BOOL)application: (NSApplication*)app printFile: (NSString*)pFile
264     (void)app;
265     std::vector<OUString> aFile { GetOUString(pFile) };
266     const ApplicationEvent* pAppEvent = new ApplicationEvent(ApplicationEvent::Type::Print, std::move(aFile));
267     AquaSalInstance::aAppEventList.push_back( pAppEvent );
268     AquaSalInstance *pInst = GetSalData()->mpInstance;
269     if( pInst )
270         pInst->TriggerUserEventProcessing();
271     return YES;
273 -(NSApplicationPrintReply)application: (NSApplication *) app printFiles:(NSArray *)files withSettings: (NSDictionary *)printSettings showPrintPanels:(BOOL)bShowPrintPanels
275     (void)app;
276     (void)printSettings;
277     (void)bShowPrintPanels;
278     // currently ignores print settings a bShowPrintPanels
279     std::vector<OUString> aFileList;
281     NSEnumerator* it = [files objectEnumerator];
282     NSString* pFile = nil;
284     while( (pFile = [it nextObject]) != nil )
285     {
286         aFileList.push_back( GetOUString( pFile ) );
287     }
288     const ApplicationEvent* pAppEvent = new ApplicationEvent(ApplicationEvent::Type::Print, std::move(aFileList));
289     AquaSalInstance::aAppEventList.push_back( pAppEvent );
290     AquaSalInstance *pInst = GetSalData()->mpInstance;
291     if( pInst )
292         pInst->TriggerUserEventProcessing();
293     // we have no back channel here, we have to assume success
294     // correct handling would be NSPrintingReplyLater and then send [app replyToOpenOrPrint]
295     return NSPrintingSuccess;
298 -(void)applicationWillTerminate: (NSNotification *) aNotification
300     (void)aNotification;
301     sal_detail_deinitialize();
302     _Exit(0);
305 -(NSApplicationTerminateReply)applicationShouldTerminate: (NSApplication *) app
307     (void)app;
309     // Related: tdf#126638 disable all menu items when displaying modal windows
310     // Although -[SalNSMenuItem validateMenuItem:] disables almost all menu
311     // items when a modal window is displayed, the standard Quit menu item
312     // does not get disabled so disable it here.
313     if ([NSApp modalWindow])
314         return NSTerminateCancel;
316     NSApplicationTerminateReply aReply = NSTerminateNow;
317     {
318         SolarMutexGuard aGuard;
320         AquaSalInstance *pInst = GetSalData()->mpInstance;
321         SalFrame *pAnyFrame = pInst->anyFrame();
322         if( pAnyFrame )
323         {
324             // the following QueryExit will likely present a message box, activate application
325             [NSApp activateIgnoringOtherApps: YES];
326             aReply = pAnyFrame->CallCallback( SalEvent::Shutdown, nullptr ) ? NSTerminateCancel : NSTerminateNow;
327         }
329         if( aReply == NSTerminateNow )
330         {
331             ApplicationEvent aEv(ApplicationEvent::Type::PrivateDoShutdown);
332             GetpApp()->AppEvent( aEv );
333             ImageTree::get().shutdown();
334             // DeInitVCL should be called in ImplSVMain - unless someone exits first which
335             // can occur in Desktop::doShutdown for example
336         }
337     }
339     return aReply;
342 -(void)observeValueForKeyPath: (NSString*) keyPath ofObject:(id)object
343                                change: (NSDictionary<NSKeyValueChangeKey, id>*)change
344                                context: (void*)context
346     [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
347     if ([keyPath isEqualToString:@"effectiveAppearance"])
348         [self systemColorsChanged: nil];
351 -(void)systemColorsChanged: (NSNotification*) pNotification
353     (void)pNotification;
354     SolarMutexGuard aGuard;
356     AquaSalInstance *pInst = GetSalData()->mpInstance;
357     SalFrame *pAnyFrame = pInst->anyFrame();
358     if(  pAnyFrame )
359         pAnyFrame->CallCallback( SalEvent::SettingsChanged, nullptr );
362 -(void)screenParametersChanged: (NSNotification*) pNotification
364     (void)pNotification;
365     SolarMutexGuard aGuard;
367     for( auto pSalFrame : GetSalData()->mpInstance->getFrames() )
368     {
369         AquaSalFrame *pFrame = static_cast<AquaSalFrame*>( pSalFrame );
370         pFrame->screenParametersChanged();
371     }
374 -(void)scrollbarVariantChanged: (NSNotification*) pNotification
376     (void)pNotification;
377     GetSalData()->mpInstance->delayedSettingsChanged( true );
380 -(void)scrollbarSettingsChanged: (NSNotification*) pNotification
382     (void)pNotification;
383     GetSalData()->mpInstance->delayedSettingsChanged( false );
386 -(void)addFallbackMenuItem: (NSMenuItem*)pNewItem
388     AquaSalMenu::addFallbackMenuItem( pNewItem );
391 -(void)removeFallbackMenuItem: (NSMenuItem*)pItem
393     AquaSalMenu::removeFallbackMenuItem( pItem );
396 -(void)addDockMenuItem: (NSMenuItem*)pNewItem
398     NSMenu* pDock = AquaSalInstance::GetDynamicDockMenu();
399     [pDock insertItem: pNewItem atIndex: [pDock numberOfItems]];
402 // for Apple Remote implementation
404 #if !HAVE_FEATURE_MACOSX_SANDBOX
405 - (void)applicationWillBecomeActive:(NSNotification *)pNotification
407     (void)pNotification;
408     SalData* pSalData = GetSalData();
409     AppleRemoteMainController* pAppleRemoteCtrl = pSalData->mpAppleRemoteMainController;
410     if( pAppleRemoteCtrl && pAppleRemoteCtrl->remoteControl)
411     {
412         // [remoteControl startListening: self];
413         // does crash because the right thing to do is
414         // [pAppleRemoteCtrl->remoteControl startListening: self];
415         // but the instance variable 'remoteControl' is declared protected
416         // workaround : declare remoteControl instance variable as public in RemoteMainController.m
418         [pAppleRemoteCtrl->remoteControl startListening: self];
419 #if OSL_DEBUG_LEVEL >= 2
420         NSLog(@"Apple Remote will become active - Using remote controls");
421 #endif
422     }
423     for( std::list< AquaSalFrame* >::const_iterator it = pSalData->maPresentationFrames.begin();
424          it != pSalData->maPresentationFrames.end(); ++it )
425     {
426         NSWindow* pNSWindow = (*it)->getNSWindow();
427         [pNSWindow setLevel: NSPopUpMenuWindowLevel];
428         if( [pNSWindow isVisible] )
429             [pNSWindow orderFront: NSApp];
430     }
433 - (void)applicationWillResignActive:(NSNotification *)pNotification
435     (void)pNotification;
436     SalData* pSalData = GetSalData();
437     AppleRemoteMainController* pAppleRemoteCtrl = pSalData->mpAppleRemoteMainController;
438     if( pAppleRemoteCtrl && pAppleRemoteCtrl->remoteControl)
439     {
440         // [remoteControl stopListening: self];
441         // does crash because the right thing to do is
442         // [pAppleRemoteCtrl->remoteControl stopListening: self];
443         // but the instance variable 'remoteControl' is declared protected
444         // workaround : declare remoteControl instance variable as public in RemoteMainController.m
446         [pAppleRemoteCtrl->remoteControl stopListening: self];
447 #if OSL_DEBUG_LEVEL >= 2
448         NSLog(@"Apple Remote will resign active - Releasing remote controls");
449 #endif
450     }
451     for( std::list< AquaSalFrame* >::const_iterator it = pSalData->maPresentationFrames.begin();
452          it != pSalData->maPresentationFrames.end(); ++it )
453     {
454         [(*it)->getNSWindow() setLevel: NSNormalWindowLevel];
455     }
457 #endif
459 - (BOOL)applicationShouldHandleReopen: (NSApplication*)pApp hasVisibleWindows: (BOOL) bWinVisible
461     (void)pApp;
462     (void)bWinVisible;
463     NSObject* pHdl = GetSalData()->mpDockIconClickHandler;
464     if( pHdl && [pHdl respondsToSelector: @selector(dockIconClicked:)] )
465     {
466         [pHdl performSelector:@selector(dockIconClicked:) withObject: self];
467     }
468     return YES;
471 - (BOOL)applicationSupportsSecureRestorableState: (NSApplication *)pApp
473     return YES;
476 -(void)setDockIconClickHandler: (NSObject*)pHandler
478     GetSalData()->mpDockIconClickHandler = pHandler;
482 @end
484 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */