1/************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25#include "precompiled_vcl.hxx" 26 27#include "rtl/ustrbuf.hxx" 28 29#include "vcl/window.hxx" 30#include "vcl/svapp.hxx" 31#include "vcl/cmdevt.hxx" 32 33#include "aqua/vclnsapp.h" 34#include "aqua/salinst.h" 35#include "aqua/saldata.hxx" 36#include "aqua/salframe.h" 37#include "aqua/salframeview.h" 38 39#include "impimagetree.hxx" 40 41#include "premac.h" 42#import "Carbon/Carbon.h" 43#import "apple_remote/RemoteControl.h" 44#include "postmac.h" 45 46 47@implementation CocoaThreadEnabler 48-(void)enableCocoaThreads:(id)param 49{ 50 // do nothing, this is just to start an NSThread and therefore put 51 // Cocoa into multithread mode 52 (void)param; 53} 54@end 55 56@implementation VCL_NSApplication 57 58-(void)applicationDidFinishLaunching:(NSNotification*)aNotification 59{ 60 NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined 61 location: NSZeroPoint 62 modifierFlags: 0 63 timestamp: 0 64 windowNumber: 0 65 context: nil 66 subtype: AquaSalInstance::AppExecuteSVMain 67 data1: 0 68 data2: 0 ]; 69 if( pEvent ) 70 [NSApp postEvent: pEvent atStart: NO]; 71} 72 73-(void)sendEvent:(NSEvent*)pEvent 74{ 75 NSEventType eType = [pEvent type]; 76 if( eType == NSApplicationDefined ) 77 GetSalData()->mpFirstInstance->handleAppDefinedEvent( pEvent ); 78 else if( eType == NSKeyDown && ([pEvent modifierFlags] & NSCommandKeyMask) != 0 ) 79 { 80 NSWindow* pKeyWin = [NSApp keyWindow]; 81 if( pKeyWin && [pKeyWin isKindOfClass: [SalFrameWindow class]] ) 82 { 83 AquaSalFrame* pFrame = [(SalFrameWindow*)pKeyWin getSalFrame]; 84 // handle Cmd-W 85 // FIXME: the correct solution would be to handle this in framework 86 // in the menu code 87 // however that is currently being revised, so let's use a preliminary solution here 88 // this hack is based on assumption 89 // a) Cmd-W is the same in all languages in OOo's menu conig 90 // b) Cmd-W is the same in all languages in on MacOS 91 // for now this seems to be true 92 unsigned int nModMask = ([pEvent modifierFlags] & (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask|NSCommandKeyMask)); 93 if( (pFrame->mnStyleMask & NSClosableWindowMask) != 0 ) 94 { 95 if( nModMask == NSCommandKeyMask 96 && [[pEvent charactersIgnoringModifiers] isEqualToString: @"w"] ) 97 { 98 [(SalFrameWindow*)pFrame->getNSWindow() windowShouldClose: nil]; 99 return; 100 } 101 } 102 103 /* 104 * #i98949# - Cmd-M miniaturize window, Cmd-Option-M miniaturize all windows 105 */ 106 if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"m"] ) 107 { 108 if ( nModMask == NSCommandKeyMask && ([pFrame->getNSWindow() styleMask] & NSMiniaturizableWindowMask) ) 109 { 110 [pFrame->getNSWindow() performMiniaturize: nil]; 111 return; 112 } 113 114 if ( nModMask == ( NSCommandKeyMask | NSAlternateKeyMask ) ) 115 { 116 [NSApp miniaturizeAll: nil]; 117 return; 118 } 119 } 120 121 // #i90083# handle frame switching 122 // FIXME: lousy workaround 123 if( (nModMask & (NSControlKeyMask|NSAlternateKeyMask)) == 0 ) 124 { 125 if( [[pEvent characters] isEqualToString: @"<"] || 126 [[pEvent characters] isEqualToString: @"~"] ) 127 { 128 [self cycleFrameForward: pFrame]; 129 return; 130 } 131 else if( [[pEvent characters] isEqualToString: @">"] || 132 [[pEvent characters] isEqualToString: @"`"] ) 133 { 134 [self cycleFrameBackward: pFrame]; 135 return; 136 } 137 } 138 139 // get information whether the event was handled; keyDown returns nothing 140 GetSalData()->maKeyEventAnswer[ pEvent ] = false; 141 bool bHandled = false; 142 143 // dispatch to view directly to avoid the key event being consumed by the menubar 144 // popup windows do not get the focus, so they don't get these either 145 // simplest would be dispatch this to the key window always if it is without parent 146 // however e.g. in document we want the menu shortcut if e.g. the stylist has focus 147 if( pFrame->mpParent && (pFrame->mnStyle & SAL_FRAME_STYLE_FLOAT) == 0 ) 148 { 149 [[pKeyWin contentView] keyDown: pEvent]; 150 bHandled = GetSalData()->maKeyEventAnswer[ pEvent ]; 151 } 152 153 // see whether the main menu consumes this event 154 // if not, we want to dispatch it ourselves. Unless we do this "trick" 155 // the main menu just beeps for an unknown or disabled key equivalent 156 // and swallows the event wholesale 157 NSMenu* pMainMenu = [NSApp mainMenu]; 158 if( ! bHandled && (pMainMenu == 0 || ! [pMainMenu performKeyEquivalent: pEvent]) ) 159 { 160 [[pKeyWin contentView] keyDown: pEvent]; 161 bHandled = GetSalData()->maKeyEventAnswer[ pEvent ]; 162 } 163 else 164 bHandled = true; // event handled already or main menu just handled it 165 166 GetSalData()->maKeyEventAnswer.erase( pEvent ); 167 if( bHandled ) 168 return; 169 } 170 else if( pKeyWin ) 171 { 172 // #i94601# a window not of vcl's making has the focus. 173 // Since our menus do not invoke the usual commands 174 // try to play nice with native windows like the file dialog 175 // and emulate them 176 // precondition: this ONLY works because CMD-V (paste), CMD-C (copy) and CMD-X (cut) are 177 // NOT localized, that is the same in all locales. Should this be 178 // different in any locale, this hack will fail. 179 unsigned int nModMask = ([pEvent modifierFlags] & (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask|NSCommandKeyMask)); 180 if( nModMask == NSCommandKeyMask ) 181 { 182 183 if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"v"] ) 184 { 185 if( [NSApp sendAction: @selector(paste:) to: nil from: nil] ) 186 return; 187 } 188 else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"c"] ) 189 { 190 if( [NSApp sendAction: @selector(copy:) to: nil from: nil] ) 191 return; 192 } 193 else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"x"] ) 194 { 195 if( [NSApp sendAction: @selector(cut:) to: nil from: nil] ) 196 return; 197 } 198 } 199 } 200 } 201 else if( eType == NSScrollWheel && ( GetSalData()->mnSystemVersion < OSX_VER_LEOPARD /* fixed in Leopard and above */ ) ) 202 { 203 204 NSWindow* pWin = [pEvent window]; 205 // on Tiger wheel events do not reach non key windows 206 // which probably should be considered a bug 207 if( [pWin isKindOfClass: [SalFrameWindow class]] && [pWin canBecomeKeyWindow] == NO ) 208 { 209 [[pWin contentView] scrollWheel: pEvent]; 210 return; 211 } 212 } 213 [super sendEvent: pEvent]; 214} 215 216-(void)sendSuperEvent:(NSEvent*)pEvent 217{ 218 [super sendEvent: pEvent]; 219} 220 221-(void)cycleFrameForward: (AquaSalFrame*)pCurFrame 222{ 223 // find current frame in list 224 std::list< AquaSalFrame* >& rFrames( GetSalData()->maFrames ); 225 std::list< AquaSalFrame* >::iterator it = rFrames.begin(); 226 for( ; it != rFrames.end() && *it != pCurFrame; ++it ) 227 ; 228 if( it != rFrames.end() ) 229 { 230 // now find the next frame (or end) 231 do 232 { 233 ++it; 234 if( it != rFrames.end() ) 235 { 236 if( (*it)->mpDockMenuEntry != NULL && 237 (*it)->mbShown ) 238 { 239 [(*it)->getNSWindow() makeKeyAndOrderFront: NSApp]; 240 return; 241 } 242 } 243 } while( it != rFrames.end() ); 244 // cycle around, find the next up to pCurFrame 245 it = rFrames.begin(); 246 while( *it != pCurFrame ) 247 { 248 if( (*it)->mpDockMenuEntry != NULL && 249 (*it)->mbShown ) 250 { 251 [(*it)->getNSWindow() makeKeyAndOrderFront: NSApp]; 252 return; 253 } 254 ++it; 255 } 256 } 257} 258 259-(void)cycleFrameBackward: (AquaSalFrame*)pCurFrame 260{ 261 // do the same as cycleFrameForward only with a reverse iterator 262 263 // find current frame in list 264 std::list< AquaSalFrame* >& rFrames( GetSalData()->maFrames ); 265 std::list< AquaSalFrame* >::reverse_iterator it = rFrames.rbegin(); 266 for( ; it != rFrames.rend() && *it != pCurFrame; ++it ) 267 ; 268 if( it != rFrames.rend() ) 269 { 270 // now find the next frame (or end) 271 do 272 { 273 ++it; 274 if( it != rFrames.rend() ) 275 { 276 if( (*it)->mpDockMenuEntry != NULL && 277 (*it)->mbShown ) 278 { 279 [(*it)->getNSWindow() makeKeyAndOrderFront: NSApp]; 280 return; 281 } 282 } 283 } while( it != rFrames.rend() ); 284 // cycle around, find the next up to pCurFrame 285 it = rFrames.rbegin(); 286 while( *it != pCurFrame ) 287 { 288 if( (*it)->mpDockMenuEntry != NULL && 289 (*it)->mbShown ) 290 { 291 [(*it)->getNSWindow() makeKeyAndOrderFront: NSApp]; 292 return; 293 } 294 ++it; 295 } 296 } 297} 298 299-(NSMenu*)applicationDockMenu:(NSApplication *)sender 300{ 301 (void)sender; 302 return AquaSalInstance::GetDynamicDockMenu(); 303} 304 305-(BOOL)application: (NSApplication*)app openFile: (NSString*)pFile 306{ 307 (void)app; 308 const rtl::OUString aFile( GetOUString( pFile ) ); 309 if( ! AquaSalInstance::isOnCommandLine( aFile ) ) 310 { 311 const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(), 312 APPEVENT_OPEN_STRING, aFile ); 313 AquaSalInstance::aAppEventList.push_back( pAppEvent ); 314 } 315 return YES; 316} 317 318-(void)application: (NSApplication*) app openFiles: (NSArray*)files 319{ 320 (void)app; 321 rtl::OUStringBuffer aFileList( 256 ); 322 323 NSEnumerator* it = [files objectEnumerator]; 324 NSString* pFile = nil; 325 326 while( (pFile = [it nextObject]) != nil ) 327 { 328 const rtl::OUString aFile( GetOUString( pFile ) ); 329 if( ! AquaSalInstance::isOnCommandLine( aFile ) ) 330 { 331 if( aFileList.getLength() > 0 ) 332 aFileList.append( sal_Unicode( APPEVENT_PARAM_DELIMITER ) ); 333 aFileList.append( aFile ); 334 } 335 } 336 337 if( aFileList.getLength() ) 338 { 339 // we have no back channel here, we have to assume success, in which case 340 // replyToOpenOrPrint does not need to be called according to documentation 341 // [app replyToOpenOrPrint: NSApplicationDelegateReplySuccess]; 342 const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(), 343 APPEVENT_OPEN_STRING, aFileList.makeStringAndClear() ); 344 AquaSalInstance::aAppEventList.push_back( pAppEvent ); 345 } 346} 347 348-(BOOL)application: (NSApplication*)app printFile: (NSString*)pFile 349{ 350 (void)app; 351 const rtl::OUString aFile( GetOUString( pFile ) ); 352 const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(), 353 APPEVENT_PRINT_STRING, aFile ); 354 AquaSalInstance::aAppEventList.push_back( pAppEvent ); 355 return YES; 356} 357-(NSApplicationPrintReply)application: (NSApplication *) app printFiles:(NSArray *)files withSettings: (NSDictionary *)printSettings showPrintPanels:(BOOL)bShowPrintPanels 358{ 359 (void)app; 360 (void)printSettings; 361 (void)bShowPrintPanels; 362 // currently ignores print settings an bShowPrintPanels 363 rtl::OUStringBuffer aFileList( 256 ); 364 365 NSEnumerator* it = [files objectEnumerator]; 366 NSString* pFile = nil; 367 368 while( (pFile = [it nextObject]) != nil ) 369 { 370 if( aFileList.getLength() > 0 ) 371 aFileList.append( sal_Unicode( APPEVENT_PARAM_DELIMITER ) ); 372 aFileList.append( GetOUString( pFile ) ); 373 } 374 const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(), 375 APPEVENT_PRINT_STRING, aFileList.makeStringAndClear() ); 376 AquaSalInstance::aAppEventList.push_back( pAppEvent ); 377 // we have no back channel here, we have to assume success 378 // correct handling would be NSPrintingReplyLater and then send [app replyToOpenOrPrint] 379 return NSPrintingSuccess; 380} 381 382-(NSApplicationTerminateReply)applicationShouldTerminate: (NSApplication *) app 383{ 384 (void)app; 385 NSApplicationTerminateReply aReply = NSTerminateNow; 386 { 387 YIELD_GUARD; 388 389 SalData* pSalData = GetSalData(); 390 if( ! pSalData->maFrames.empty() ) 391 { 392 // the following QueryExit will likely present a message box, activate application 393 [NSApp activateIgnoringOtherApps: YES]; 394 aReply = pSalData->maFrames.front()->CallCallback( SALEVENT_SHUTDOWN, NULL ) ? NSTerminateCancel : NSTerminateNow; 395 } 396 397 if( aReply == NSTerminateNow ) 398 { 399 ApplicationEvent aEv( String(), ApplicationAddress(), ByteString( "PRIVATE:DOSHUTDOWN" ), String() ); 400 GetpApp()->AppEvent( aEv ); 401 ImplImageTreeSingletonRef()->shutDown(); 402 // DeInitVCL should be called in ImplSVMain - unless someon _exits first which 403 // can occur in Desktop::doShutdown for example 404 } 405 } 406 407 return aReply; 408} 409 410-(void)systemColorsChanged: (NSNotification*) pNotification 411{ 412 (void)pNotification; 413 YIELD_GUARD; 414 415 const SalData* pSalData = GetSalData(); 416 if( !pSalData->maFrames.empty() ) 417 pSalData->maFrames.front()->CallCallback( SALEVENT_SETTINGSCHANGED, NULL ); 418} 419 420-(void)screenParametersChanged: (NSNotification*) pNotification 421{ 422 (void)pNotification; 423 YIELD_GUARD; 424 425 SalData* pSalData = GetSalData(); 426 std::list< AquaSalFrame* >::iterator it; 427 for( it = pSalData->maFrames.begin(); it != pSalData->maFrames.end(); ++it ) 428 { 429 (*it)->screenParametersChanged(); 430 } 431} 432 433-(void)scrollbarVariantChanged: (NSNotification*) pNotification 434{ 435 (void)pNotification; 436 GetSalData()->mpFirstInstance->delayedSettingsChanged( true ); 437} 438 439-(void)scrollbarSettingsChanged: (NSNotification*) pNotification 440{ 441 (void)pNotification; 442 GetSalData()->mpFirstInstance->delayedSettingsChanged( false ); 443} 444 445-(void)addFallbackMenuItem: (NSMenuItem*)pNewItem 446{ 447 AquaSalMenu::addFallbackMenuItem( pNewItem ); 448} 449 450-(void)removeFallbackMenuItem: (NSMenuItem*)pItem 451{ 452 AquaSalMenu::removeFallbackMenuItem( pItem ); 453} 454 455-(void)addDockMenuItem: (NSMenuItem*)pNewItem 456{ 457 NSMenu* pDock = AquaSalInstance::GetDynamicDockMenu(); 458 [pDock insertItem: pNewItem atIndex: [pDock numberOfItems]]; 459} 460 461// for Apple Remote implementation 462 463#pragma mark - 464#pragma mark NSApplication Delegates 465- (void)applicationWillBecomeActive:(NSNotification *)pNotification 466{ 467 (void)pNotification; 468 SalData* pSalData = GetSalData(); 469 AppleRemoteMainController* pAppleRemoteCtrl = pSalData->mpAppleRemoteMainController; 470 if( pAppleRemoteCtrl && pAppleRemoteCtrl->remoteControl) 471 { 472 // [remoteControl startListening: self]; 473 // does crash because the right thing to do is 474 // [pAppleRemoteCtrl->remoteControl startListening: self]; 475 // but the instance variable 'remoteControl' is declared protected 476 // workaround : declare remoteControl instance variable as public in RemoteMainController.m 477 478 [pAppleRemoteCtrl->remoteControl startListening: self]; 479#ifdef DEBUG 480 NSLog(@"Apple Remote will become active - Using remote controls"); 481#endif 482 } 483 for( std::list< AquaSalFrame* >::const_iterator it = pSalData->maPresentationFrames.begin(); 484 it != pSalData->maPresentationFrames.end(); ++it ) 485 { 486 NSWindow* pNSWindow = (*it)->getNSWindow(); 487 [pNSWindow setLevel: NSPopUpMenuWindowLevel]; 488 if( [pNSWindow isVisible] ) 489 [pNSWindow orderFront: NSApp]; 490 } 491} 492 493- (void)applicationWillResignActive:(NSNotification *)pNotification 494{ 495 (void)pNotification; 496 SalData* pSalData = GetSalData(); 497 AppleRemoteMainController* pAppleRemoteCtrl = pSalData->mpAppleRemoteMainController; 498 if( pAppleRemoteCtrl && pAppleRemoteCtrl->remoteControl) 499 { 500 // [remoteControl stopListening: self]; 501 // does crash because the right thing to do is 502 // [pAppleRemoteCtrl->remoteControl stopListening: self]; 503 // but the instance variable 'remoteControl' is declared protected 504 // workaround : declare remoteControl instance variable as public in RemoteMainController.m 505 506 [pAppleRemoteCtrl->remoteControl stopListening: self]; 507#ifdef DEBUG 508 NSLog(@"Apple Remote will resign active - Releasing remote controls"); 509#endif 510 } 511 for( std::list< AquaSalFrame* >::const_iterator it = pSalData->maPresentationFrames.begin(); 512 it != pSalData->maPresentationFrames.end(); ++it ) 513 { 514 [(*it)->getNSWindow() setLevel: NSNormalWindowLevel]; 515 } 516} 517 518- (BOOL)applicationShouldHandleReopen: (NSApplication*)pApp hasVisibleWindows: (BOOL) bWinVisible 519{ 520 (void)pApp; 521 (void)bWinVisible; 522 NSObject* pHdl = GetSalData()->mpDockIconClickHandler; 523 if( pHdl && [pHdl respondsToSelector: @selector(dockIconClicked:)] ) 524 { 525 [pHdl performSelector:@selector(dockIconClicked:) withObject: self]; 526 } 527 return YES; 528} 529 530-(void)setDockIconClickHandler: (NSObject*)pHandler 531{ 532 GetSalData()->mpDockIconClickHandler = pHandler; 533} 534 535 536@end 537 538