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 <stdio.h> 28 29 #include "tools/fsys.hxx" 30 #include "tools/getprocessworkingdir.hxx" 31 #include <tools/solarmutex.hxx> 32 33 #include "osl/process.h" 34 35 #include "rtl/ustrbuf.hxx" 36 37 #include "vcl/svapp.hxx" 38 #include "vcl/window.hxx" 39 #include "vcl/timer.hxx" 40 41 #include "aqua/saldata.hxx" 42 #include "aqua/salinst.h" 43 #include "aqua/salframe.h" 44 #include "aqua/salobj.h" 45 #include "aqua/salsys.h" 46 #include "aqua/salvd.h" 47 #include "aqua/salbmp.h" 48 #include "aqua/salprn.h" 49 #include "aqua/saltimer.h" 50 #include "aqua/vclnsapp.h" 51 52 #include "print.h" 53 #include "impbmp.hxx" 54 #include "salimestatus.hxx" 55 56 #include <comphelper/processfactory.hxx> 57 58 #include <com/sun/star/beans/XPropertySet.hpp> 59 #include <com/sun/star/lang/XMultiServiceFactory.hpp> 60 #include <com/sun/star/uri/XExternalUriReferenceTranslator.hpp> 61 #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp> 62 #include <com/sun/star/uno/XComponentContext.hpp> 63 64 #include "premac.h" 65 #include <Foundation/Foundation.h> 66 #include <ApplicationServices/ApplicationServices.h> 67 #import "apple_remote/RemoteMainController.h" 68 #include "apple_remote/RemoteControl.h" 69 #include "postmac.h" 70 71 using namespace std; 72 using namespace ::com::sun::star; 73 74 extern sal_Bool ImplSVMain(); 75 76 static sal_Bool* gpbInit = 0; 77 static NSMenu* pDockMenu = nil; 78 static bool bNoSVMain = true; 79 static bool bLeftMain = false; 80 // ----------------------------------------------------------------------- 81 82 class AquaDelayedSettingsChanged : public Timer 83 { 84 bool mbInvalidate; 85 public: 86 AquaDelayedSettingsChanged( bool bInvalidate ) : 87 mbInvalidate( bInvalidate ) 88 { 89 } 90 91 virtual void Timeout() 92 { 93 SalData* pSalData = GetSalData(); 94 if( ! pSalData->maFrames.empty() ) 95 pSalData->maFrames.front()->CallCallback( SALEVENT_SETTINGSCHANGED, NULL ); 96 97 if( mbInvalidate ) 98 { 99 for( std::list< AquaSalFrame* >::iterator it = pSalData->maFrames.begin(); 100 it != pSalData->maFrames.end(); ++it ) 101 { 102 if( (*it)->mbShown ) 103 (*it)->SendPaintEvent( NULL ); 104 } 105 } 106 Stop(); 107 delete this; 108 } 109 }; 110 111 void AquaSalInstance::delayedSettingsChanged( bool bInvalidate ) 112 { 113 vos::OGuard aGuard( *mpSalYieldMutex ); 114 AquaDelayedSettingsChanged* pTimer = new AquaDelayedSettingsChanged( bInvalidate ); 115 pTimer->SetTimeout( 50 ); 116 pTimer->Start(); 117 } 118 119 120 // the AppEventList must be available before any SalData/SalInst/etc. objects are ready 121 typedef std::list<const ApplicationEvent*> AppEventList; 122 AppEventList AquaSalInstance::aAppEventList; 123 124 NSMenu* AquaSalInstance::GetDynamicDockMenu() 125 { 126 if( ! pDockMenu && ! bLeftMain ) 127 pDockMenu = [[NSMenu alloc] initWithTitle: @""]; 128 return pDockMenu; 129 } 130 131 bool AquaSalInstance::isOnCommandLine( const rtl::OUString& rArg ) 132 { 133 sal_uInt32 nArgs = osl_getCommandArgCount(); 134 for( sal_uInt32 i = 0; i < nArgs; i++ ) 135 { 136 rtl::OUString aArg; 137 osl_getCommandArg( i, &aArg.pData ); 138 if( aArg.equals( rArg ) ) 139 return true; 140 } 141 return false; 142 } 143 144 145 // initialize the cocoa VCL_NSApplication object 146 // returns an NSAutoreleasePool that must be released when the event loop begins 147 static void initNSApp() 148 { 149 // create our cocoa NSApplication 150 [VCL_NSApplication sharedApplication]; 151 152 SalData::ensureThreadAutoreleasePool(); 153 154 // put cocoa into multithreaded mode 155 [NSThread detachNewThreadSelector:@selector(enableCocoaThreads:) toTarget:[[CocoaThreadEnabler alloc] init] withObject:nil]; 156 157 // activate our delegate methods 158 [NSApp setDelegate: NSApp]; 159 160 [[NSNotificationCenter defaultCenter] addObserver: NSApp 161 selector: @selector(systemColorsChanged:) 162 name: NSSystemColorsDidChangeNotification 163 object: nil ]; 164 [[NSNotificationCenter defaultCenter] addObserver: NSApp 165 selector: @selector(screenParametersChanged:) 166 name: NSApplicationDidChangeScreenParametersNotification 167 object: nil ]; 168 // add observers for some settings changes that affect vcl's settings 169 // scrollbar variant 170 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp 171 selector: @selector(scrollbarVariantChanged:) 172 name: @"AppleAquaScrollBarVariantChanged" 173 object: nil ]; 174 // scrollbar page behavior ("jump to here" or not) 175 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp 176 selector: @selector(scrollbarSettingsChanged:) 177 name: @"AppleNoRedisplayAppearancePreferenceChanged" 178 object: nil ]; 179 180 // get System Version and store the value in GetSalData()->mnSystemVersion 181 OSErr err = noErr; 182 SInt32 systemVersion = VER_TIGER; // Initialize with minimal requirement 183 if( (err = Gestalt(gestaltSystemVersion, &systemVersion)) == noErr ) 184 { 185 GetSalData()->mnSystemVersion = systemVersion; 186 #if OSL_DEBUG_LEVEL > 1 187 fprintf( stderr, "System Version %x\n", (unsigned int)systemVersion); 188 #endif 189 } 190 else 191 NSLog(@"Unable to obtain system version: %ld", (long)err); 192 193 // Initialize Apple Remote 194 GetSalData()->mpMainController = [[MainController alloc] init]; 195 196 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp 197 selector: @selector(applicationWillBecomeActive:) 198 name: @"AppleRemoteWillBecomeActive" 199 object: nil ]; 200 201 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp 202 selector: @selector(applicationWillResignActive:) 203 name: @"AppleRemoteWillResignActive" 204 object: nil ]; 205 206 if( ImplGetSVData()->mbIsTestTool ) 207 [NSApp activateIgnoringOtherApps: YES]; 208 } 209 210 sal_Bool ImplSVMainHook( sal_Bool * pbInit ) 211 { 212 gpbInit = pbInit; 213 214 bNoSVMain = false; 215 initNSApp(); 216 217 NSPoint aPt = { 0, 0 }; 218 NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined 219 location: aPt 220 modifierFlags: 0 221 timestamp: 0 222 windowNumber: 0 223 context: nil 224 subtype: AquaSalInstance::AppExecuteSVMain 225 data1: 0 226 data2: 0 ]; 227 if( pEvent ) 228 { 229 [NSApp postEvent: pEvent atStart: NO]; 230 231 rtl::OUString aExeURL, aExe; 232 osl_getExecutableFile( &aExeURL.pData ); 233 osl_getSystemPathFromFileURL( aExeURL.pData, &aExe.pData ); 234 rtl::OString aByteExe( rtl::OUStringToOString( aExe, osl_getThreadTextEncoding() ) ); 235 236 #ifdef DEBUG 237 aByteExe += OString ( " NSAccessibilityDebugLogLevel 1" ); 238 const char* pArgv[] = { aByteExe.getStr(), NULL }; 239 NSApplicationMain( 3, pArgv ); 240 #else 241 const char* pArgv[] = { aByteExe.getStr(), NULL }; 242 NSApplicationMain( 1, pArgv ); 243 #endif 244 } 245 else 246 { 247 DBG_ERROR( "NSApplication initialization could not be done" ); 248 } 249 250 return TRUE; // indicate that ImplSVMainHook is implemented 251 } 252 253 // ======================================================================= 254 255 void SalAbort( const XubString& rErrorText ) 256 { 257 if( !rErrorText.Len() ) 258 fprintf( stderr, "Application Error " ); 259 else 260 fprintf( stderr, "%s ", 261 ByteString( rErrorText, gsl_getSystemTextEncoding() ).GetBuffer() ); 262 abort(); 263 } 264 265 // ----------------------------------------------------------------------- 266 267 void InitSalData() 268 { 269 SalData *pSalData = new SalData; 270 SetSalData( pSalData ); 271 } 272 273 // ----------------------------------------------------------------------- 274 275 const ::rtl::OUString& SalGetDesktopEnvironment() 276 { 277 static OUString aDesktopEnvironment(RTL_CONSTASCII_USTRINGPARAM( "MacOSX" )); 278 return aDesktopEnvironment; 279 } 280 281 // ----------------------------------------------------------------------- 282 283 void DeInitSalData() 284 { 285 SalData *pSalData = GetSalData(); 286 if( pSalData->mpStatusItem ) 287 { 288 [pSalData->mpStatusItem release]; 289 pSalData->mpStatusItem = nil; 290 } 291 delete pSalData; 292 SetSalData( NULL ); 293 } 294 295 // ----------------------------------------------------------------------- 296 297 extern "C" { 298 #include <crt_externs.h> 299 } 300 301 // ----------------------------------------------------------------------- 302 303 void InitSalMain() 304 { 305 rtl::OUString urlWorkDir; 306 rtl_uString *sysWorkDir = NULL; 307 if (tools::getProcessWorkingDir(&urlWorkDir)) 308 { 309 oslFileError err2 = osl_getSystemPathFromFileURL(urlWorkDir.pData, &sysWorkDir); 310 if (err2 == osl_File_E_None) 311 { 312 ByteString aPath( getenv( "PATH" ) ); 313 ByteString aResPath( getenv( "STAR_RESOURCEPATH" ) ); 314 ByteString aLibPath( getenv( "DYLD_LIBRARY_PATH" ) ); 315 ByteString aCmdPath( OUStringToOString(OUString(sysWorkDir), RTL_TEXTENCODING_UTF8).getStr() ); 316 ByteString aTmpPath; 317 // Get absolute path of command's directory 318 if ( aCmdPath.Len() ) { 319 DirEntry aCmdDirEntry( aCmdPath ); 320 aCmdDirEntry.ToAbs(); 321 aCmdPath = ByteString( aCmdDirEntry.GetPath().GetFull(), RTL_TEXTENCODING_ASCII_US ); 322 } 323 // Assign to PATH environment variable 324 if ( aCmdPath.Len() ) 325 { 326 aTmpPath = ByteString( "PATH=" ); 327 aTmpPath += aCmdPath; 328 if ( aPath.Len() ) 329 aTmpPath += ByteString( DirEntry::GetSearchDelimiter(), RTL_TEXTENCODING_ASCII_US ); 330 aTmpPath += aPath; 331 putenv( (char*)aTmpPath.GetBuffer() ); 332 } 333 // Assign to STAR_RESOURCEPATH environment variable 334 if ( aCmdPath.Len() ) 335 { 336 aTmpPath = ByteString( "STAR_RESOURCEPATH=" ); 337 aTmpPath += aCmdPath; 338 if ( aResPath.Len() ) 339 aTmpPath += ByteString( DirEntry::GetSearchDelimiter(), RTL_TEXTENCODING_ASCII_US ); 340 aTmpPath += aResPath; 341 putenv( (char*)aTmpPath.GetBuffer() ); 342 } 343 // Assign to DYLD_LIBRARY_PATH environment variable 344 if ( aCmdPath.Len() ) 345 { 346 aTmpPath = ByteString( "DYLD_LIBRARY_PATH=" ); 347 aTmpPath += aCmdPath; 348 if ( aLibPath.Len() ) 349 aTmpPath += ByteString( DirEntry::GetSearchDelimiter(), RTL_TEXTENCODING_ASCII_US ); 350 aTmpPath += aLibPath; 351 putenv( (char*)aTmpPath.GetBuffer() ); 352 } 353 } 354 } 355 } 356 357 // ----------------------------------------------------------------------- 358 359 void DeInitSalMain() 360 { 361 } 362 363 // ======================================================================= 364 365 SalYieldMutex::SalYieldMutex() 366 { 367 mnCount = 0; 368 mnThreadId = 0; 369 } 370 371 void SalYieldMutex::acquire() 372 { 373 OMutex::acquire(); 374 mnThreadId = vos::OThread::getCurrentIdentifier(); 375 mnCount++; 376 } 377 378 void SalYieldMutex::release() 379 { 380 if ( mnThreadId == vos::OThread::getCurrentIdentifier() ) 381 { 382 if ( mnCount == 1 ) 383 mnThreadId = 0; 384 mnCount--; 385 } 386 OMutex::release(); 387 } 388 389 sal_Bool SalYieldMutex::tryToAcquire() 390 { 391 if ( OMutex::tryToAcquire() ) 392 { 393 mnThreadId = vos::OThread::getCurrentIdentifier(); 394 mnCount++; 395 return sal_True; 396 } 397 else 398 return sal_False; 399 } 400 401 // ----------------------------------------------------------------------- 402 403 // some convenience functions regarding the yield mutex, aka solar mutex 404 405 sal_Bool ImplSalYieldMutexTryToAcquire() 406 { 407 AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance; 408 if ( pInst ) 409 return pInst->mpSalYieldMutex->tryToAcquire(); 410 else 411 return FALSE; 412 } 413 414 void ImplSalYieldMutexAcquire() 415 { 416 AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance; 417 if ( pInst ) 418 pInst->mpSalYieldMutex->acquire(); 419 } 420 421 void ImplSalYieldMutexRelease() 422 { 423 AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance; 424 if ( pInst ) 425 pInst->mpSalYieldMutex->release(); 426 } 427 428 // ======================================================================= 429 430 SalInstance* CreateSalInstance() 431 { 432 // this is the case for not using SVMain 433 // not so good 434 if( bNoSVMain ) 435 initNSApp(); 436 437 SalData* pSalData = GetSalData(); 438 DBG_ASSERT( pSalData->mpFirstInstance == NULL, "more than one instance created" ); 439 AquaSalInstance* pInst = new AquaSalInstance; 440 441 // init instance (only one instance in this version !!!) 442 pSalData->mpFirstInstance = pInst; 443 // this one is for outside AquaSalInstance::Yield 444 SalData::ensureThreadAutoreleasePool(); 445 // no focus rects on NWF aqua 446 ImplGetSVData()->maNWFData.mbNoFocusRects = true; 447 ImplGetSVData()->maNWFData.mbNoBoldTabFocus = true; 448 ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise = true; 449 ImplGetSVData()->maNWFData.mbCenteredTabs = true; 450 ImplGetSVData()->maNWFData.mbProgressNeedsErase = true; 451 ImplGetSVData()->maNWFData.mbCheckBoxNeedsErase = true; 452 ImplGetSVData()->maNWFData.mnStatusBarLowerRightOffset = 10; 453 ImplGetSVData()->maGDIData.mbNoXORClipping = true; 454 ImplGetSVData()->maWinData.mbNoSaveBackground = true; 455 456 return pInst; 457 } 458 459 // ----------------------------------------------------------------------- 460 461 void DestroySalInstance( SalInstance* pInst ) 462 { 463 delete pInst; 464 } 465 466 // ----------------------------------------------------------------------- 467 468 AquaSalInstance::AquaSalInstance() 469 { 470 mpSalYieldMutex = new SalYieldMutex; 471 mpSalYieldMutex->acquire(); 472 ::tools::SolarMutex::SetSolarMutex( mpSalYieldMutex ); 473 maMainThread = vos::OThread::getCurrentIdentifier(); 474 mbWaitingYield = false; 475 maUserEventListMutex = osl_createMutex(); 476 mnActivePrintJobs = 0; 477 maWaitingYieldCond = osl_createCondition(); 478 } 479 480 // ----------------------------------------------------------------------- 481 482 AquaSalInstance::~AquaSalInstance() 483 { 484 ::tools::SolarMutex::SetSolarMutex( 0 ); 485 mpSalYieldMutex->release(); 486 delete mpSalYieldMutex; 487 osl_destroyMutex( maUserEventListMutex ); 488 osl_destroyCondition( maWaitingYieldCond ); 489 } 490 491 // ----------------------------------------------------------------------- 492 493 void AquaSalInstance::wakeupYield() 494 { 495 // wakeup :Yield 496 if( mbWaitingYield ) 497 { 498 SalData::ensureThreadAutoreleasePool(); 499 NSPoint aPt = { 0, 0 }; 500 NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined 501 location: aPt 502 modifierFlags: 0 503 timestamp: 0 504 windowNumber: 0 505 context: nil 506 subtype: AquaSalInstance::YieldWakeupEvent 507 data1: 0 508 data2: 0 ]; 509 if( pEvent ) 510 [NSApp postEvent: pEvent atStart: NO]; 511 } 512 } 513 514 // ----------------------------------------------------------------------- 515 516 void AquaSalInstance::PostUserEvent( AquaSalFrame* pFrame, sal_uInt16 nType, void* pData ) 517 { 518 osl_acquireMutex( maUserEventListMutex ); 519 maUserEvents.push_back( SalUserEvent( pFrame, pData, nType ) ); 520 osl_releaseMutex( maUserEventListMutex ); 521 522 // notify main loop that an event has arrived 523 wakeupYield(); 524 } 525 526 // ----------------------------------------------------------------------- 527 528 vos::IMutex* AquaSalInstance::GetYieldMutex() 529 { 530 return mpSalYieldMutex; 531 } 532 533 // ----------------------------------------------------------------------- 534 535 sal_uLong AquaSalInstance::ReleaseYieldMutex() 536 { 537 SalYieldMutex* pYieldMutex = mpSalYieldMutex; 538 if ( pYieldMutex->GetThreadId() == 539 vos::OThread::getCurrentIdentifier() ) 540 { 541 sal_uLong nCount = pYieldMutex->GetAcquireCount(); 542 sal_uLong n = nCount; 543 while ( n ) 544 { 545 pYieldMutex->release(); 546 n--; 547 } 548 549 return nCount; 550 } 551 else 552 return 0; 553 } 554 555 // ----------------------------------------------------------------------- 556 557 void AquaSalInstance::AcquireYieldMutex( sal_uLong nCount ) 558 { 559 SalYieldMutex* pYieldMutex = mpSalYieldMutex; 560 while ( nCount ) 561 { 562 pYieldMutex->acquire(); 563 nCount--; 564 } 565 } 566 567 // ----------------------------------------------------------------------- 568 569 bool AquaSalInstance::CheckYieldMutex() 570 { 571 bool bRet = true; 572 573 SalYieldMutex* pYieldMutex = mpSalYieldMutex; 574 if ( pYieldMutex->GetThreadId() != 575 vos::OThread::getCurrentIdentifier() ) 576 { 577 bRet = false; 578 } 579 580 return bRet; 581 } 582 583 // ----------------------------------------------------------------------- 584 585 bool AquaSalInstance::isNSAppThread() const 586 { 587 return vos::OThread::getCurrentIdentifier() == maMainThread; 588 } 589 590 // ----------------------------------------------------------------------- 591 592 void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent ) 593 { 594 switch( [pEvent subtype] ) 595 { 596 case AppStartTimerEvent: 597 AquaSalTimer::handleStartTimerEvent( pEvent ); 598 break; 599 case AppEndLoopEvent: 600 [NSApp stop: NSApp]; 601 break; 602 case AppExecuteSVMain: 603 { 604 sal_Bool bResult = ImplSVMain(); 605 if( gpbInit ) 606 *gpbInit = bResult; 607 [NSApp stop: NSApp]; 608 bLeftMain = true; 609 if( pDockMenu ) 610 { 611 [pDockMenu release]; 612 pDockMenu = nil; 613 } 614 } 615 break; 616 case AppleRemoteEvent: 617 { 618 sal_Int16 nCommand = 0; 619 SalData* pSalData = GetSalData(); 620 bool bIsFullScreenMode = false; 621 622 std::list<AquaSalFrame*>::iterator it = pSalData->maFrames.begin(); 623 while( (*it) && ( (it != pSalData->maFrames.end() ) || ( (*it)->mbFullScreen == false ) ) ) 624 { 625 if( (*it)->mbFullScreen ) 626 bIsFullScreenMode = true; 627 it++; 628 } 629 630 switch ([pEvent data1]) 631 { 632 case kRemoteButtonPlay: 633 nCommand = ( bIsFullScreenMode == true ) ? MEDIA_COMMAND_PLAY_PAUSE : MEDIA_COMMAND_PLAY; 634 break; 635 636 // kept for experimentation purpose (scheduled for future implementation) 637 // case kRemoteButtonMenu: nCommand = MEDIA_COMMAND_MENU; break; 638 639 case kRemoteButtonPlus: nCommand = MEDIA_COMMAND_VOLUME_UP; break; 640 641 case kRemoteButtonMinus: nCommand = MEDIA_COMMAND_VOLUME_DOWN; break; 642 643 case kRemoteButtonRight: nCommand = MEDIA_COMMAND_NEXTTRACK; break; 644 645 case kRemoteButtonRight_Hold: nCommand = MEDIA_COMMAND_NEXTTRACK_HOLD; break; 646 647 case kRemoteButtonLeft: nCommand = MEDIA_COMMAND_PREVIOUSTRACK; break; 648 649 case kRemoteButtonLeft_Hold: nCommand = MEDIA_COMMAND_REWIND; break; 650 651 case kRemoteButtonPlay_Hold: nCommand = MEDIA_COMMAND_PLAY_HOLD; break; 652 653 case kRemoteButtonMenu_Hold: nCommand = MEDIA_COMMAND_STOP; break; 654 655 // FIXME : not detected 656 case kRemoteButtonPlus_Hold: 657 case kRemoteButtonMinus_Hold: 658 break; 659 660 default: 661 break; 662 } 663 AquaSalFrame* pFrame = pSalData->maFrames.front(); 664 Window * pWindow = pFrame->GetWindow() ? pSalData->maFrames.front()->GetWindow() : NULL; 665 666 if( pWindow ) 667 { 668 const Point aPoint; 669 CommandEvent aCEvt( aPoint, COMMAND_MEDIA, FALSE, &nCommand ); 670 NotifyEvent aNCmdEvt( EVENT_COMMAND, pWindow, &aCEvt ); 671 672 if ( !ImplCallPreNotify( aNCmdEvt ) ) 673 pWindow->Command( aCEvt ); 674 } 675 676 } 677 break; 678 679 case YieldWakeupEvent: 680 // do nothing, fall out of Yield 681 break; 682 683 default: 684 DBG_ERROR( "unhandled NSApplicationDefined event" ); 685 break; 686 }; 687 } 688 689 // ----------------------------------------------------------------------- 690 691 class ReleasePoolHolder 692 { 693 NSAutoreleasePool* mpPool; 694 public: 695 ReleasePoolHolder() : mpPool( [[NSAutoreleasePool alloc] init] ) {} 696 ~ReleasePoolHolder() { [mpPool release]; } 697 }; 698 699 void AquaSalInstance::Yield( bool bWait, bool bHandleAllCurrentEvents ) 700 { 701 // ensure that the per thread autorelease pool is top level and 702 // will therefore not be destroyed by cocoa implicitly 703 SalData::ensureThreadAutoreleasePool(); 704 705 // NSAutoreleasePool documentation suggests we should have 706 // an own pool for each yield level 707 ReleasePoolHolder aReleasePool; 708 709 // Release all locks so that we don't deadlock when we pull pending 710 // events from the event queue 711 bool bDispatchUser = true; 712 while( bDispatchUser ) 713 { 714 sal_uLong nCount = ReleaseYieldMutex(); 715 716 // get one user event 717 osl_acquireMutex( maUserEventListMutex ); 718 SalUserEvent aEvent( NULL, NULL, 0 ); 719 if( ! maUserEvents.empty() ) 720 { 721 aEvent = maUserEvents.front(); 722 maUserEvents.pop_front(); 723 } 724 else 725 bDispatchUser = false; 726 osl_releaseMutex( maUserEventListMutex ); 727 728 AcquireYieldMutex( nCount ); 729 730 // dispatch it 731 if( aEvent.mpFrame && AquaSalFrame::isAlive( aEvent.mpFrame ) ) 732 { 733 aEvent.mpFrame->CallCallback( aEvent.mnType, aEvent.mpData ); 734 osl_setCondition( maWaitingYieldCond ); 735 // return if only one event is asked for 736 if( ! bHandleAllCurrentEvents ) 737 return; 738 } 739 } 740 741 // handle cocoa event queue 742 // cocoa events mye be only handled in the thread the NSApp was created 743 if( isNSAppThread() && mnActivePrintJobs == 0 ) 744 { 745 // we need to be woken up by a cocoa-event 746 // if a user event should be posted by the event handling below 747 bool bOldWaitingYield = mbWaitingYield; 748 mbWaitingYield = bWait; 749 750 // handle available events 751 NSEvent* pEvent = nil; 752 bool bHadEvent = false; 753 do 754 { 755 sal_uLong nCount = ReleaseYieldMutex(); 756 757 pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: nil 758 inMode: NSDefaultRunLoopMode dequeue: YES]; 759 if( pEvent ) 760 { 761 [NSApp sendEvent: pEvent]; 762 bHadEvent = true; 763 } 764 [NSApp updateWindows]; 765 766 AcquireYieldMutex( nCount ); 767 } while( bHandleAllCurrentEvents && pEvent ); 768 769 // if we had no event yet, wait for one if requested 770 if( bWait && ! bHadEvent ) 771 { 772 sal_uLong nCount = ReleaseYieldMutex(); 773 774 NSDate* pDt = AquaSalTimer::pRunningTimer ? [AquaSalTimer::pRunningTimer fireDate] : [NSDate distantFuture]; 775 pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: pDt 776 inMode: NSDefaultRunLoopMode dequeue: YES]; 777 if( pEvent ) 778 [NSApp sendEvent: pEvent]; 779 [NSApp updateWindows]; 780 781 AcquireYieldMutex( nCount ); 782 783 // #i86581# 784 // FIXME: sometimes the NSTimer will never fire. Firing it by hand then 785 // fixes the problem even seems to set the correct next firing date 786 // Why oh why ? 787 if( ! pEvent && AquaSalTimer::pRunningTimer ) 788 { 789 // this cause crashes on MacOSX 10.4 790 // [AquaSalTimer::pRunningTimer fire]; 791 ImplGetSVData()->mpSalTimer->CallCallback(); 792 } 793 } 794 795 mbWaitingYield = bOldWaitingYield; 796 797 // collect update rectangles 798 const std::list< AquaSalFrame* > rFrames( GetSalData()->maFrames ); 799 for( std::list< AquaSalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it ) 800 { 801 if( (*it)->mbShown && ! (*it)->maInvalidRect.IsEmpty() ) 802 { 803 (*it)->Flush( (*it)->maInvalidRect ); 804 (*it)->maInvalidRect.SetEmpty(); 805 } 806 } 807 osl_setCondition( maWaitingYieldCond ); 808 } 809 else if( bWait ) 810 { 811 // #i103162# 812 // wait until any thread (most likely the main thread) 813 // has dispatched an event, cop out at 200 ms 814 osl_resetCondition( maWaitingYieldCond ); 815 TimeValue aVal = { 0, 200000000 }; 816 sal_uLong nCount = ReleaseYieldMutex(); 817 osl_waitCondition( maWaitingYieldCond, &aVal ); 818 AcquireYieldMutex( nCount ); 819 } 820 821 // we get some apple events way too early 822 // before the application is ready to handle them, 823 // so their corresponding application events need to be delayed 824 // now is a good time to handle at least one of them 825 if( bWait && !aAppEventList.empty() && ImplGetSVData()->maAppData.mbInAppExecute ) 826 { 827 // make sure that only one application event is active at a time 828 static bool bInAppEvent = false; 829 if( !bInAppEvent ) 830 { 831 bInAppEvent = true; 832 // get the next delayed application event 833 const ApplicationEvent* pAppEvent = aAppEventList.front(); 834 aAppEventList.pop_front(); 835 // handle one application event (no recursion) 836 const ImplSVData* pSVData = ImplGetSVData(); 837 pSVData->mpApp->AppEvent( *pAppEvent ); 838 delete pAppEvent; 839 // allow the next delayed application event 840 bInAppEvent = false; 841 } 842 } 843 } 844 845 // ----------------------------------------------------------------------- 846 847 bool AquaSalInstance::AnyInput( sal_uInt16 nType ) 848 { 849 if( nType & INPUT_APPEVENT ) 850 { 851 if( ! aAppEventList.empty() ) 852 return true; 853 if( nType == INPUT_APPEVENT ) 854 return false; 855 } 856 857 if( nType & INPUT_TIMER ) 858 { 859 if( AquaSalTimer::pRunningTimer ) 860 { 861 NSDate* pDt = [AquaSalTimer::pRunningTimer fireDate]; 862 if( pDt && [pDt timeIntervalSinceNow] < 0 ) 863 { 864 return true; 865 } 866 } 867 } 868 869 unsigned/*NSUInteger*/ nEventMask = 0; 870 if( nType & INPUT_MOUSE) 871 nEventMask |= 872 NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask | 873 NSLeftMouseUpMask | NSRightMouseUpMask | NSOtherMouseUpMask | 874 NSLeftMouseDraggedMask | NSRightMouseDraggedMask | NSOtherMouseDraggedMask | 875 NSScrollWheelMask | 876 // NSMouseMovedMask | 877 NSMouseEnteredMask | NSMouseExitedMask; 878 if( nType & INPUT_KEYBOARD) 879 nEventMask |= NSKeyDownMask | NSKeyUpMask | NSFlagsChangedMask; 880 if( nType & INPUT_OTHER) 881 nEventMask |= NSTabletPoint; 882 // TODO: INPUT_PAINT / more INPUT_OTHER 883 if( !nType) 884 return false; 885 886 NSEvent* pEvent = [NSApp nextEventMatchingMask: nEventMask untilDate: nil 887 inMode: NSDefaultRunLoopMode dequeue: NO]; 888 return (pEvent != NULL); 889 } 890 891 // ----------------------------------------------------------------------- 892 893 SalFrame* AquaSalInstance::CreateChildFrame( SystemParentData*, sal_uLong /*nSalFrameStyle*/ ) 894 { 895 return NULL; 896 } 897 898 // ----------------------------------------------------------------------- 899 900 SalFrame* AquaSalInstance::CreateFrame( SalFrame* pParent, sal_uLong nSalFrameStyle ) 901 { 902 SalData::ensureThreadAutoreleasePool(); 903 904 SalFrame* pFrame = new AquaSalFrame( pParent, nSalFrameStyle ); 905 return pFrame; 906 } 907 908 // ----------------------------------------------------------------------- 909 910 void AquaSalInstance::DestroyFrame( SalFrame* pFrame ) 911 { 912 delete pFrame; 913 } 914 915 // ----------------------------------------------------------------------- 916 917 SalObject* AquaSalInstance::CreateObject( SalFrame* pParent, SystemWindowData* /* pWindowData */, sal_Bool /* bShow */ ) 918 { 919 // SystemWindowData is meaningless on Mac OS X 920 AquaSalObject *pObject = NULL; 921 922 if ( pParent ) 923 pObject = new AquaSalObject( static_cast<AquaSalFrame*>(pParent) ); 924 925 return pObject; 926 } 927 928 // ----------------------------------------------------------------------- 929 930 void AquaSalInstance::DestroyObject( SalObject* pObject ) 931 { 932 delete ( pObject ); 933 } 934 935 // ----------------------------------------------------------------------- 936 937 SalPrinter* AquaSalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter ) 938 { 939 return new AquaSalPrinter( dynamic_cast<AquaSalInfoPrinter*>(pInfoPrinter) ); 940 } 941 942 // ----------------------------------------------------------------------- 943 944 void AquaSalInstance::DestroyPrinter( SalPrinter* pPrinter ) 945 { 946 delete pPrinter; 947 } 948 949 // ----------------------------------------------------------------------- 950 951 void AquaSalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList ) 952 { 953 NSArray* pNames = [NSPrinter printerNames]; 954 NSArray* pTypes = [NSPrinter printerTypes]; 955 unsigned int nNameCount = pNames ? [pNames count] : 0; 956 unsigned int nTypeCount = pTypes ? [pTypes count] : 0; 957 DBG_ASSERT( nTypeCount == nNameCount, "type count not equal to printer count" ); 958 for( unsigned int i = 0; i < nNameCount; i++ ) 959 { 960 NSString* pName = [pNames objectAtIndex: i]; 961 NSString* pType = i < nTypeCount ? [pTypes objectAtIndex: i] : nil; 962 if( pName ) 963 { 964 SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo; 965 pInfo->maPrinterName = GetOUString( pName ); 966 if( pType ) 967 pInfo->maDriver = GetOUString( pType ); 968 pInfo->mnStatus = 0; 969 pInfo->mnJobs = 0; 970 pInfo->mpSysData = NULL; 971 972 pList->Add( pInfo ); 973 } 974 } 975 } 976 977 // ----------------------------------------------------------------------- 978 979 void AquaSalInstance::GetPrinterQueueState( SalPrinterQueueInfo* ) 980 { 981 } 982 983 // ----------------------------------------------------------------------- 984 985 void AquaSalInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo ) 986 { 987 delete pInfo; 988 } 989 990 // ----------------------------------------------------------------------- 991 992 XubString AquaSalInstance::GetDefaultPrinter() 993 { 994 // #i113170# may not be the main thread if called from UNO API 995 SalData::ensureThreadAutoreleasePool(); 996 997 if( ! maDefaultPrinter.getLength() ) 998 { 999 NSPrintInfo* pPI = [NSPrintInfo sharedPrintInfo]; 1000 DBG_ASSERT( pPI, "no print info" ); 1001 if( pPI ) 1002 { 1003 NSPrinter* pPr = [pPI printer]; 1004 DBG_ASSERT( pPr, "no printer in default info" ); 1005 if( pPr ) 1006 { 1007 NSString* pDefName = [pPr name]; 1008 DBG_ASSERT( pDefName, "printer has no name" ); 1009 maDefaultPrinter = GetOUString( pDefName ); 1010 } 1011 } 1012 } 1013 return maDefaultPrinter; 1014 } 1015 1016 // ----------------------------------------------------------------------- 1017 1018 SalInfoPrinter* AquaSalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo, 1019 ImplJobSetup* pSetupData ) 1020 { 1021 // #i113170# may not be the main thread if called from UNO API 1022 SalData::ensureThreadAutoreleasePool(); 1023 1024 SalInfoPrinter* pNewInfoPrinter = NULL; 1025 if( pQueueInfo ) 1026 { 1027 pNewInfoPrinter = new AquaSalInfoPrinter( *pQueueInfo ); 1028 if( pSetupData ) 1029 pNewInfoPrinter->SetPrinterData( pSetupData ); 1030 } 1031 1032 return pNewInfoPrinter; 1033 } 1034 1035 // ----------------------------------------------------------------------- 1036 1037 void AquaSalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter ) 1038 { 1039 // #i113170# may not be the main thread if called from UNO API 1040 SalData::ensureThreadAutoreleasePool(); 1041 1042 delete pPrinter; 1043 } 1044 1045 // ----------------------------------------------------------------------- 1046 1047 SalSystem* AquaSalInstance::CreateSystem() 1048 { 1049 return new AquaSalSystem(); 1050 } 1051 1052 // ----------------------------------------------------------------------- 1053 1054 void AquaSalInstance::DestroySystem( SalSystem* pSystem ) 1055 { 1056 delete pSystem; 1057 } 1058 1059 // ----------------------------------------------------------------------- 1060 1061 void AquaSalInstance::SetEventCallback( void*, bool(*)(void*,void*,int) ) 1062 { 1063 } 1064 1065 // ----------------------------------------------------------------------- 1066 1067 void AquaSalInstance::SetErrorEventCallback( void*, bool(*)(void*,void*,int) ) 1068 { 1069 } 1070 1071 // ----------------------------------------------------------------------- 1072 1073 void* AquaSalInstance::GetConnectionIdentifier( ConnectionIdentifierType& rReturnedType, int& rReturnedBytes ) 1074 { 1075 rReturnedBytes = 1; 1076 rReturnedType = AsciiCString; 1077 return (void*)""; 1078 } 1079 1080 // We need to re-encode file urls because osl_getFileURLFromSystemPath converts 1081 // to UTF-8 before encoding non ascii characters, which is not what other apps expect. 1082 static rtl::OUString translateToExternalUrl(const rtl::OUString& internalUrl) 1083 { 1084 rtl::OUString extUrl; 1085 1086 uno::Reference< lang::XMultiServiceFactory > sm = comphelper::getProcessServiceFactory(); 1087 if (sm.is()) 1088 { 1089 uno::Reference< beans::XPropertySet > pset; 1090 sm->queryInterface( getCppuType( &pset )) >>= pset; 1091 if (pset.is()) 1092 { 1093 uno::Reference< uno::XComponentContext > context; 1094 static const rtl::OUString DEFAULT_CONTEXT( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ) ); 1095 pset->getPropertyValue(DEFAULT_CONTEXT) >>= context; 1096 if (context.is()) 1097 extUrl = uri::ExternalUriReferenceTranslator::create(context)->translateToExternal(internalUrl); 1098 } 1099 } 1100 return extUrl; 1101 } 1102 1103 // #i104525# many versions of OSX have problems with some URLs: 1104 // when an app requests OSX to add one of these URLs to the "Recent Items" list 1105 // then this app gets killed (TextEdit, Preview, etc. and also OOo) 1106 static bool isDangerousUrl( const rtl::OUString& rUrl ) 1107 { 1108 // use a heuristic that detects all known cases since there is no official comment 1109 // on the exact impact and root cause of the OSX bug 1110 const int nLen = rUrl.getLength(); 1111 const sal_Unicode* p = rUrl.getStr(); 1112 for( int i = 0; i < nLen-3; ++i, ++p ) { 1113 if( p[0] != '%' ) 1114 continue; 1115 // escaped percent? 1116 if( (p[1] == '2') && (p[2] == '5') ) 1117 return true; 1118 // escapes are considered to be UTF-8 encoded 1119 // => check for invalid UTF-8 leading byte 1120 if( (p[1] != 'f') && (p[1] != 'F') ) 1121 continue; 1122 int cLowNibble = p[2]; 1123 if( (cLowNibble >= '0' ) && (cLowNibble <= '9')) 1124 return false; 1125 if( cLowNibble >= 'a' ) 1126 cLowNibble -= 'a' - 'A'; 1127 if( (cLowNibble < 'A') || (cLowNibble >= 'C')) 1128 return true; 1129 } 1130 1131 return false; 1132 } 1133 1134 void AquaSalInstance::AddToRecentDocumentList(const rtl::OUString& rFileUrl, const rtl::OUString& /*rMimeType*/) 1135 { 1136 // Convert file URL for external use (see above) 1137 rtl::OUString externalUrl = translateToExternalUrl(rFileUrl); 1138 if( 0 == externalUrl.getLength() ) 1139 externalUrl = rFileUrl; 1140 1141 if( externalUrl.getLength() && !isDangerousUrl( externalUrl ) ) 1142 { 1143 NSString* pString = CreateNSString( externalUrl ); 1144 NSURL* pURL = [NSURL URLWithString: pString]; 1145 1146 if( pURL ) 1147 { 1148 NSDocumentController* pCtrl = [NSDocumentController sharedDocumentController]; 1149 [pCtrl noteNewRecentDocumentURL: pURL]; 1150 } 1151 if( pString ) 1152 [pString release]; 1153 } 1154 } 1155 1156 1157 // ----------------------------------------------------------------------- 1158 1159 SalTimer* AquaSalInstance::CreateSalTimer() 1160 { 1161 return new AquaSalTimer(); 1162 } 1163 1164 // ----------------------------------------------------------------------- 1165 1166 SalSystem* AquaSalInstance::CreateSalSystem() 1167 { 1168 return new AquaSalSystem(); 1169 } 1170 1171 // ----------------------------------------------------------------------- 1172 1173 SalBitmap* AquaSalInstance::CreateSalBitmap() 1174 { 1175 return new AquaSalBitmap(); 1176 } 1177 1178 // ----------------------------------------------------------------------- 1179 1180 SalSession* AquaSalInstance::CreateSalSession() 1181 { 1182 return NULL; 1183 } 1184 1185 // ----------------------------------------------------------------------- 1186 1187 class MacImeStatus : public SalI18NImeStatus 1188 { 1189 public: 1190 MacImeStatus() {} 1191 virtual ~MacImeStatus() {} 1192 1193 // asks whether there is a status window available 1194 // to toggle into menubar 1195 virtual bool canToggle() { return false; } 1196 virtual void toggle() {} 1197 }; 1198 1199 // ----------------------------------------------------------------------- 1200 1201 SalI18NImeStatus* AquaSalInstance::CreateI18NImeStatus() 1202 { 1203 return new MacImeStatus(); 1204 } 1205 1206 // YieldMutexReleaser 1207 YieldMutexReleaser::YieldMutexReleaser() : mnCount( 0 ) 1208 { 1209 SalData* pSalData = GetSalData(); 1210 if( ! pSalData->mpFirstInstance->isNSAppThread() ) 1211 { 1212 SalData::ensureThreadAutoreleasePool(); 1213 mnCount = pSalData->mpFirstInstance->ReleaseYieldMutex(); 1214 } 1215 } 1216 1217 YieldMutexReleaser::~YieldMutexReleaser() 1218 { 1219 if( mnCount != 0 ) 1220 GetSalData()->mpFirstInstance->AcquireYieldMutex( mnCount ); 1221 } 1222 1223 ////////////////////////////////////////////////////////////// 1224 rtl::OUString GetOUString( CFStringRef rStr ) 1225 { 1226 if( rStr == 0 ) 1227 return rtl::OUString(); 1228 CFIndex nLength = CFStringGetLength( rStr ); 1229 if( nLength == 0 ) 1230 return rtl::OUString(); 1231 const UniChar* pConstStr = CFStringGetCharactersPtr( rStr ); 1232 if( pConstStr ) 1233 return rtl::OUString( pConstStr, nLength ); 1234 UniChar* pStr = reinterpret_cast<UniChar*>( rtl_allocateMemory( sizeof(UniChar)*nLength ) ); 1235 CFRange aRange = { 0, nLength }; 1236 CFStringGetCharacters( rStr, aRange, pStr ); 1237 rtl::OUString aRet( pStr, nLength ); 1238 rtl_freeMemory( pStr ); 1239 return aRet; 1240 } 1241 1242 rtl::OUString GetOUString( NSString* pStr ) 1243 { 1244 if( ! pStr ) 1245 return rtl::OUString(); 1246 int nLen = [pStr length]; 1247 if( nLen == 0 ) 1248 return rtl::OUString(); 1249 1250 rtl::OUStringBuffer aBuf( nLen+1 ); 1251 aBuf.setLength( nLen ); 1252 [pStr getCharacters: const_cast<sal_Unicode*>(aBuf.getStr())]; 1253 return aBuf.makeStringAndClear(); 1254 } 1255 1256 CFStringRef CreateCFString( const rtl::OUString& rStr ) 1257 { 1258 return CFStringCreateWithCharacters(kCFAllocatorDefault, rStr.getStr(), rStr.getLength() ); 1259 } 1260 1261 NSString* CreateNSString( const rtl::OUString& rStr ) 1262 { 1263 return [[NSString alloc] initWithCharacters: rStr.getStr() length: rStr.getLength()]; 1264 } 1265 1266 CGImageRef CreateCGImage( const Image& rImage ) 1267 { 1268 BitmapEx aBmpEx( rImage.GetBitmapEx() ); 1269 Bitmap aBmp( aBmpEx.GetBitmap() ); 1270 1271 if( ! aBmp || ! aBmp.ImplGetImpBitmap() ) 1272 return NULL; 1273 1274 // simple case, no transparency 1275 AquaSalBitmap* pSalBmp = static_cast<AquaSalBitmap*>(aBmp.ImplGetImpBitmap()->ImplGetSalBitmap()); 1276 1277 if( ! pSalBmp ) 1278 return NULL; 1279 1280 CGImageRef xImage = NULL; 1281 if( ! (aBmpEx.IsAlpha() || aBmpEx.IsTransparent() ) ) 1282 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); 1283 else if( aBmpEx.IsAlpha() ) 1284 { 1285 AlphaMask aAlphaMask( aBmpEx.GetAlpha() ); 1286 Bitmap aMask( aAlphaMask.GetBitmap() ); 1287 AquaSalBitmap* pMaskBmp = static_cast<AquaSalBitmap*>(aMask.ImplGetImpBitmap()->ImplGetSalBitmap()); 1288 if( pMaskBmp ) 1289 xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); 1290 else 1291 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); 1292 } 1293 else if( aBmpEx.GetTransparentType() == TRANSPARENT_BITMAP ) 1294 { 1295 Bitmap aMask( aBmpEx.GetMask() ); 1296 AquaSalBitmap* pMaskBmp = static_cast<AquaSalBitmap*>(aMask.ImplGetImpBitmap()->ImplGetSalBitmap()); 1297 if( pMaskBmp ) 1298 xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); 1299 else 1300 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); 1301 } 1302 else if( aBmpEx.GetTransparentType() == TRANSPARENT_COLOR ) 1303 { 1304 Color aTransColor( aBmpEx.GetTransparentColor() ); 1305 SalColor nTransColor = MAKE_SALCOLOR( aTransColor.GetRed(), aTransColor.GetGreen(), aTransColor.GetBlue() ); 1306 xImage = pSalBmp->CreateColorMask( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight, nTransColor ); 1307 } 1308 1309 return xImage; 1310 } 1311 1312 NSImage* CreateNSImage( const Image& rImage ) 1313 { 1314 CGImageRef xImage = CreateCGImage( rImage ); 1315 1316 if( ! xImage ) 1317 return nil; 1318 1319 Size aSize( rImage.GetSizePixel() ); 1320 NSImage* pImage = [[NSImage alloc] initWithSize: NSMakeSize( aSize.Width(), aSize.Height() )]; 1321 if( pImage ) 1322 { 1323 [pImage setFlipped: YES]; 1324 [pImage lockFocus]; 1325 1326 NSGraphicsContext* pContext = [NSGraphicsContext currentContext]; 1327 CGContextRef rCGContext = reinterpret_cast<CGContextRef>([pContext graphicsPort]); 1328 1329 const CGRect aDstRect = CGRectMake( 0, 0, aSize.Width(), aSize.Height()); 1330 CGContextDrawImage( rCGContext, aDstRect, xImage ); 1331 1332 [pImage unlockFocus]; 1333 } 1334 1335 CGImageRelease( xImage ); 1336 1337 return pImage; 1338 } 1339