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