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