1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: vclnsapp.mm,v $
10 * $Revision: 1.8.46.3 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_vcl.hxx"
36 #include "saldata.hxx"
38 #include "salframeview.h"
40 #include "vcl/window.hxx"
41 #include "vcl/svapp.hxx"
42 #include "vcl/cmdevt.hxx"
43 #include "rtl/ustrbuf.hxx"
46 #import "Carbon/Carbon.h"
47 #import "apple_remote/RemoteControl.h"
51 @implementation CocoaThreadEnabler
52 -(void)enableCocoaThreads:(id)param
54 // do nothing, this is just to start an NSThread and therefore put
55 // Cocoa into multithread mode
59 @implementation VCL_NSApplication
60 -(void)sendEvent:(NSEvent*)pEvent
62 NSEventType eType = [pEvent type];
63 if( eType == NSApplicationDefined )
64 GetSalData()->mpFirstInstance->handleAppDefinedEvent( pEvent );
65 else if( eType == NSKeyDown && ([pEvent modifierFlags] & NSCommandKeyMask) != 0 )
67 NSWindow* pKeyWin = [NSApp keyWindow];
68 if( pKeyWin && [pKeyWin isKindOfClass: [SalFrameWindow class]] )
70 AquaSalFrame* pFrame = [(SalFrameWindow*)pKeyWin getSalFrame];
72 // FIXME: the correct solution would be to handle this in framework
74 // however that is currently being revised, so let's use a preliminary solution here
75 // this hack is based on assumption
76 // a) Cmd-W is the same in all languages in OOo's menu conig
77 // b) Cmd-W is the same in all languages in on MacOS
78 // for now this seems to be true
79 unsigned int nModMask = ([pEvent modifierFlags] & (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask|NSCommandKeyMask));
80 if( (pFrame->mnStyleMask & NSClosableWindowMask) != 0 )
82 if( nModMask == NSCommandKeyMask
83 && [[pEvent charactersIgnoringModifiers] isEqualToString: @"w"] )
85 [pFrame->getWindow() windowShouldClose: nil];
91 * #i98949# - Cmd-M miniaturize window, Cmd-Option-M miniaturize all windows
93 if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"m"] )
95 if ( nModMask == NSCommandKeyMask && ([pFrame->getWindow() styleMask] & NSMiniaturizableWindowMask) )
97 [pFrame->getWindow() performMiniaturize: nil];
101 if ( nModMask == ( NSCommandKeyMask | NSAlternateKeyMask ) )
103 [NSApp miniaturizeAll: nil];
108 // get information whether the event was handled; keyDown returns nothing
109 GetSalData()->maKeyEventAnswer[ pEvent ] = false;
110 bool bHandled = false;
112 // dispatch to view directly to avoid the key event being consumed by the menubar
113 // popup windows do not get the focus, so they don't get these either
114 // simplest would be dispatch this to the key window always if it is without parent
115 // however e.g. in document we want the menu shortcut if e.g. the stylist has focus
116 if( pFrame->mpParent && (pFrame->mnStyle & SAL_FRAME_STYLE_FLOAT) == 0 )
118 [[pKeyWin contentView] keyDown: pEvent];
119 bHandled = GetSalData()->maKeyEventAnswer[ pEvent ];
122 // see whether the main menu consumes this event
123 // if not, we want to dispatch it ourselves. Unless we do this "trick"
124 // the main menu just beeps for an unknown or disabled key equivalent
125 // and swallows the event wholesale
126 NSMenu* pMainMenu = [NSApp mainMenu];
127 if( ! bHandled && (pMainMenu == 0 || ! [pMainMenu performKeyEquivalent: pEvent]) )
129 [[pKeyWin contentView] keyDown: pEvent];
130 bHandled = GetSalData()->maKeyEventAnswer[ pEvent ];
133 bHandled = true; // event handled already or main menu just handled it
135 GetSalData()->maKeyEventAnswer.erase( pEvent );
141 // #i94601# a window not of vcl's making has the focus.
142 // Since our menus do not invoke the usual commands
143 // try to play nice with native windows like the file dialog
145 // precondition: this ONLY works because CMD-V (paste), CMD-C (copy) and CMD-X (cut) are
146 // NOT localized, that is the same in all locales. Should this be
147 // different in any locale, this hack will fail.
148 unsigned int nModMask = ([pEvent modifierFlags] & (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask|NSCommandKeyMask));
149 if( nModMask == NSCommandKeyMask )
152 if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"v"] )
154 if( [NSApp sendAction: @selector(paste:) to: nil from: nil] )
157 else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"c"] )
159 if( [NSApp sendAction: @selector(copy:) to: nil from: nil] )
162 else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"x"] )
164 if( [NSApp sendAction: @selector(cut:) to: nil from: nil] )
170 else if( eType == NSScrollWheel && ( GetSalData()->mnSystemVersion < VER_LEOPARD /* fixed in Leopard and above */ ) )
173 NSWindow* pWin = [pEvent window];
174 // on Tiger wheel events do not reach non key windows
175 // which probably should be considered a bug
176 if( [pWin isKindOfClass: [SalFrameWindow class]] && [pWin canBecomeKeyWindow] == NO )
178 [[pWin contentView] scrollWheel: pEvent];
182 [super sendEvent: pEvent];
185 -(void)sendSuperEvent:(NSEvent*)pEvent
187 [super sendEvent: pEvent];
190 -(NSMenu*)applicationDockMenu:(NSApplication *)sender
192 return AquaSalInstance::GetDynamicDockMenu();
195 -(MacOSBOOL)application: (NSApplication*)app openFile: (NSString*)pFile
197 const rtl::OUString aFile( GetOUString( pFile ) );
198 if( ! AquaSalInstance::isOnCommandLine( aFile ) )
200 const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(),
201 APPEVENT_OPEN_STRING, aFile );
202 AquaSalInstance::aAppEventList.push_back( pAppEvent );
207 -(void)application: (NSApplication*) app openFiles: (NSArray*)files
209 rtl::OUStringBuffer aFileList( 256 );
211 NSEnumerator* it = [files objectEnumerator];
212 NSString* pFile = nil;
214 while( (pFile = [it nextObject]) != nil )
216 const rtl::OUString aFile( GetOUString( pFile ) );
217 if( ! AquaSalInstance::isOnCommandLine( aFile ) )
219 if( aFileList.getLength() > 0 )
220 aFileList.append( sal_Unicode( APPEVENT_PARAM_DELIMITER ) );
221 aFileList.append( aFile );
225 if( aFileList.getLength() )
227 // we have no back channel here, we have to assume success, in which case
228 // replyToOpenOrPrint does not need to be called according to documentation
229 // [app replyToOpenOrPrint: NSApplicationDelegateReplySuccess];
230 const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(),
231 APPEVENT_OPEN_STRING, aFileList.makeStringAndClear() );
232 AquaSalInstance::aAppEventList.push_back( pAppEvent );
236 -(MacOSBOOL)application: (NSApplication*)app printFile: (NSString*)pFile
238 const rtl::OUString aFile( GetOUString( pFile ) );
239 const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(),
240 APPEVENT_PRINT_STRING, aFile );
241 AquaSalInstance::aAppEventList.push_back( pAppEvent );
244 -(NSApplicationPrintReply)application: (NSApplication *) app printFiles:(NSArray *)files withSettings: (NSDictionary *)printSettings showPrintPanels:(MacOSBOOL)bShowPrintPanels
246 // currently ignores print settings an bShowPrintPanels
247 rtl::OUStringBuffer aFileList( 256 );
249 NSEnumerator* it = [files objectEnumerator];
250 NSString* pFile = nil;
252 while( (pFile = [it nextObject]) != nil )
254 if( aFileList.getLength() > 0 )
255 aFileList.append( sal_Unicode( APPEVENT_PARAM_DELIMITER ) );
256 aFileList.append( GetOUString( pFile ) );
258 const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(),
259 APPEVENT_PRINT_STRING, aFileList.makeStringAndClear() );
260 AquaSalInstance::aAppEventList.push_back( pAppEvent );
261 // we have no back channel here, we have to assume success
262 // correct handling would be NSPrintingReplyLater and then send [app replyToOpenOrPrint]
263 return NSPrintingSuccess;
266 -(NSApplicationTerminateReply)applicationShouldTerminate: (NSApplication *) app
268 SalData* pSalData = GetSalData();
269 #if 1 // currently do some really bad hack
270 if( ! pSalData->maFrames.empty() )
272 /* #i92766# something really weird is going on with the retain count of
273 our windows; sometimes we get a duplicate free before exit on one of our
274 NSWindows. The reason is unclear; to avoid this currently we retain them once more
276 FIXME: this is a really bad hack, relying on the system to catch the leaked
277 resources. Find out what really goes on here and fix it !
279 std::vector< NSWindow* > aHackRetainedWindows;
280 for( std::list< AquaSalFrame* >::iterator it = pSalData->maFrames.begin();
281 it != pSalData->maFrames.end(); ++it )
283 #if OSL_DEBUG_LEVEL > 1
284 Window* pWin = (*it)->GetWindow();
285 String aTitle = pWin->GetText();
286 Window* pClient = pWin->ImplGetClientWindow();
287 fprintf( stderr, "retaining %p (old count %d) windowtype=%s clienttyp=%s title=%s\n",
288 (*it)->mpWindow, [(*it)->mpWindow retainCount],
289 typeid(*pWin).name(), pClient ? typeid(*pClient).name() : "<nil>",
290 rtl::OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ).getStr()
293 [(*it)->mpWindow retain];
294 aHackRetainedWindows.push_back( (*it)->mpWindow );
296 if( pSalData->maFrames.front()->CallCallback( SALEVENT_SHUTDOWN, NULL ) )
298 for( std::vector< NSWindow* >::iterator it = aHackRetainedWindows.begin();
299 it != aHackRetainedWindows.end(); ++it )
301 // clean up the retaing count again from the shutdown workaround
302 #if OSL_DEBUG_LEVEL > 1
303 fprintf( stderr, "releasing %p\n", (*it) );
307 return NSTerminateCancel;
309 #if OSL_DEBUG_LEVEL > 1
310 for( std::list< AquaSalFrame* >::iterator it = pSalData->maFrames.begin();
311 it != pSalData->maFrames.end(); ++it )
313 Window* pWin = (*it)->GetWindow();
314 String aTitle = pWin->GetText();
315 Window* pClient = pWin->ImplGetClientWindow();
316 fprintf( stderr, "frame still alive: NSWindow %p windowtype=%s clienttyp=%s title=%s\n",
317 (*it)->mpWindow, typeid(*pWin).name(), pClient ? typeid(*pClient).name() : "<nil>",
318 rtl::OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ).getStr()
323 #else // the clean version follows
324 return pSalData->maFrames.front()->CallCallback( SALEVENT_SHUTDOWN, NULL ) ? NSTerminateCancel : NSTerminateNow;
326 return NSTerminateNow;
329 -(void)systemColorsChanged: (NSNotification*) pNotification
331 const SalData* pSalData = GetSalData();
332 if( !pSalData->maFrames.empty() )
333 pSalData->maFrames.front()->CallCallback( SALEVENT_SETTINGSCHANGED, NULL );
336 -(void)screenParametersChanged: (NSNotification*) pNotification
338 SalData* pSalData = GetSalData();
339 std::list< AquaSalFrame* >::iterator it;
340 for( it = pSalData->maFrames.begin(); it != pSalData->maFrames.end(); ++it )
342 (*it)->screenParametersChanged();
346 -(void)scrollbarVariantChanged: (NSNotification*) pNotification
348 GetSalData()->mpFirstInstance->delayedSettingsChanged( true );
351 -(void)scrollbarSettingsChanged: (NSNotification*) pNotification
353 GetSalData()->mpFirstInstance->delayedSettingsChanged( false );
356 -(void)addFallbackMenuItem: (NSMenuItem*)pNewItem
358 AquaSalMenu::addFallbackMenuItem( pNewItem );
361 -(void)removeFallbackMenuItem: (NSMenuItem*)pItem
363 AquaSalMenu::removeFallbackMenuItem( pItem );
366 -(void)addDockMenuItem: (NSMenuItem*)pNewItem
368 NSMenu* pDock = AquaSalInstance::GetDynamicDockMenu();
369 [pDock insertItem: pNewItem atIndex: [pDock numberOfItems]];
372 // for Apple Remote implementation
375 #pragma mark NSApplication Delegates
376 - (void)applicationWillBecomeActive:(NSNotification *)aNotification {
377 if (GetSalData()->mpMainController->remoteControl) {
379 // [remoteControl startListening: self];
380 // does crash because the right thing to do is
381 // [GetSalData()->mpMainController->remoteControl startListening: self];
382 // but the instance variable 'remoteControl' is declared protected
383 // workaround : declare remoteControl instance variable as public in RemoteMainController.m
385 [GetSalData()->mpMainController->remoteControl startListening: self];
387 NSLog(@"Apple Remote will become active - Using remote controls");
392 - (void)applicationWillResignActive:(NSNotification *)aNotification {
393 if (GetSalData()->mpMainController->remoteControl) {
395 // [remoteControl stopListening: self];
396 // does crash because the right thing to do is
397 // [GetSalData()->mpMainController->remoteControl stopListening: self];
398 // but the instance variable 'remoteControl' is declared protected
399 // workaround : declare remoteControl instance variable as public in RemoteMainController.m
401 [GetSalData()->mpMainController->remoteControl stopListening: self];
403 NSLog(@"Apple Remote will resign active - Releasing remote controls");
408 - (MacOSBOOL)applicationShouldHandleReopen: (NSApplication*)pApp hasVisibleWindows: (MacOSBOOL) bWinVisible
410 NSObject* pHdl = GetSalData()->mpDockIconClickHandler;
411 if( pHdl && [pHdl respondsToSelector: @selector(dockIconClicked:)] )
413 [pHdl performSelector:@selector(dockIconClicked:) withObject: self];
418 -(void)setDockIconClickHandler: (NSObject*)pHandler
420 GetSalData()->mpDockIconClickHandler = pHandler;