xref: /aoo41x/main/vcl/aqua/source/app/salinst.cxx (revision f0625723)
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     // Initialize Apple Remote
194     GetSalData()->mpMainController = [[MainController alloc] init]; // TODO: rename MainController to AppleRemoteController
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         for(; it != pSalData->maFrames.end(); ++it )
624         {
625             if( (*it)->mbFullScreen )
626                 bIsFullScreenMode = true;
627         }
628 
629         switch ([pEvent data1])
630         {
631             case kRemoteButtonPlay:
632                 nCommand = ( bIsFullScreenMode == true ) ? MEDIA_COMMAND_PLAY_PAUSE : MEDIA_COMMAND_PLAY;
633                 break;
634 
635             // kept for experimentation purpose (scheduled for future implementation)
636             // case kRemoteButtonMenu:         nCommand = MEDIA_COMMAND_MENU; break;
637 
638             case kRemoteButtonPlus:     	nCommand = MEDIA_COMMAND_VOLUME_UP; break;
639 
640             case kRemoteButtonMinus:        nCommand = MEDIA_COMMAND_VOLUME_DOWN; break;
641 
642             case kRemoteButtonRight:        nCommand = MEDIA_COMMAND_NEXTTRACK; break;
643 
644             case kRemoteButtonRight_Hold:   nCommand = MEDIA_COMMAND_NEXTTRACK_HOLD; break;
645 
646             case kRemoteButtonLeft:         nCommand = MEDIA_COMMAND_PREVIOUSTRACK; break;
647 
648             case kRemoteButtonLeft_Hold:    nCommand = MEDIA_COMMAND_REWIND; break;
649 
650             case kRemoteButtonPlay_Hold:    nCommand = MEDIA_COMMAND_PLAY_HOLD; break;
651 
652             case kRemoteButtonMenu_Hold:    nCommand = MEDIA_COMMAND_STOP; break;
653 
654             // FIXME : not detected
655             case kRemoteButtonPlus_Hold:
656             case kRemoteButtonMinus_Hold:
657                 break;
658 
659             default:
660                 break;
661         }
662         AquaSalFrame* pFrame = pSalData->maFrames.front();
663         Window* pWindow = pFrame ? pFrame->GetWindow() : NULL;
664 
665         if( pWindow )
666         {
667             const Point aPoint;
668             CommandEvent aCEvt( aPoint, COMMAND_MEDIA, FALSE, &nCommand );
669             NotifyEvent aNCmdEvt( EVENT_COMMAND, pWindow, &aCEvt );
670 
671             if ( !ImplCallPreNotify( aNCmdEvt ) )
672                 pWindow->Command( aCEvt );
673         }
674 
675     }
676 	break;
677 
678     case YieldWakeupEvent:
679         // do nothing, fall out of Yield
680 	break;
681 
682     default:
683         DBG_ERROR( "unhandled NSApplicationDefined event" );
684         break;
685     };
686 }
687 
688 // -----------------------------------------------------------------------
689 
690 class ReleasePoolHolder
691 {
692     NSAutoreleasePool* mpPool;
693     public:
694     ReleasePoolHolder() : mpPool( [[NSAutoreleasePool alloc] init] ) {}
695     ~ReleasePoolHolder() { [mpPool release]; }
696 };
697 
698 void AquaSalInstance::Yield( bool bWait, bool bHandleAllCurrentEvents )
699 {
700     // ensure that the per thread autorelease pool is top level and
701     // will therefore not be destroyed by cocoa implicitly
702     SalData::ensureThreadAutoreleasePool();
703 
704     // NSAutoreleasePool documentation suggests we should have
705     // an own pool for each yield level
706     ReleasePoolHolder aReleasePool;
707 
708 	// Release all locks so that we don't deadlock when we pull pending
709 	// events from the event queue
710     bool bDispatchUser = true;
711     while( bDispatchUser )
712     {
713         sal_uLong nCount = ReleaseYieldMutex();
714 
715         // get one user event
716         osl_acquireMutex( maUserEventListMutex );
717         SalUserEvent aEvent( NULL, NULL, 0 );
718         if( ! maUserEvents.empty() )
719         {
720             aEvent = maUserEvents.front();
721             maUserEvents.pop_front();
722         }
723         else
724             bDispatchUser = false;
725         osl_releaseMutex( maUserEventListMutex );
726 
727         AcquireYieldMutex( nCount );
728 
729         // dispatch it
730         if( aEvent.mpFrame && AquaSalFrame::isAlive( aEvent.mpFrame ) )
731         {
732             aEvent.mpFrame->CallCallback( aEvent.mnType, aEvent.mpData );
733             osl_setCondition( maWaitingYieldCond );
734             // return if only one event is asked for
735             if( ! bHandleAllCurrentEvents )
736                 return;
737         }
738     }
739 
740     // handle cocoa event queue
741     // cocoa events mye be only handled in the thread the NSApp was created
742     if( isNSAppThread() && mnActivePrintJobs == 0 )
743     {
744         // we need to be woken up by a cocoa-event
745         // if a user event should be posted by the event handling below
746         bool bOldWaitingYield = mbWaitingYield;
747         mbWaitingYield = bWait;
748 
749         // handle available events
750         NSEvent* pEvent = nil;
751         bool bHadEvent = false;
752         do
753         {
754             sal_uLong nCount = ReleaseYieldMutex();
755 
756             pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: nil
757                             inMode: NSDefaultRunLoopMode dequeue: YES];
758             if( pEvent )
759             {
760                 [NSApp sendEvent: pEvent];
761                 bHadEvent = true;
762             }
763             [NSApp updateWindows];
764 
765             AcquireYieldMutex( nCount );
766         } while( bHandleAllCurrentEvents && pEvent );
767 
768         // if we had no event yet, wait for one if requested
769         if( bWait && ! bHadEvent )
770         {
771             sal_uLong nCount = ReleaseYieldMutex();
772 
773             NSDate* pDt = AquaSalTimer::pRunningTimer ? [AquaSalTimer::pRunningTimer fireDate] : [NSDate distantFuture];
774             pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: pDt
775                             inMode: NSDefaultRunLoopMode dequeue: YES];
776             if( pEvent )
777                 [NSApp sendEvent: pEvent];
778             [NSApp updateWindows];
779 
780             AcquireYieldMutex( nCount );
781 
782             // #i86581#
783             // FIXME: sometimes the NSTimer will never fire. Firing it by hand then
784             // fixes the problem even seems to set the correct next firing date
785             // Why oh why ?
786             if( ! pEvent && AquaSalTimer::pRunningTimer )
787             {
788                 // this cause crashes on MacOSX 10.4
789                 // [AquaSalTimer::pRunningTimer fire];
790                 ImplGetSVData()->mpSalTimer->CallCallback();
791             }
792         }
793 
794         mbWaitingYield = bOldWaitingYield;
795 
796         // collect update rectangles
797         const std::list< AquaSalFrame* > rFrames( GetSalData()->maFrames );
798         for( std::list< AquaSalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it )
799         {
800             if( (*it)->mbShown && ! (*it)->maInvalidRect.IsEmpty() )
801             {
802                 (*it)->Flush( (*it)->maInvalidRect );
803                 (*it)->maInvalidRect.SetEmpty();
804             }
805         }
806         osl_setCondition( maWaitingYieldCond );
807     }
808     else if( bWait )
809     {
810         // #i103162#
811         // wait until any thread (most likely the main thread)
812         // has dispatched an event, cop out at 200 ms
813         osl_resetCondition( maWaitingYieldCond );
814         TimeValue aVal = { 0, 200000000 };
815         sal_uLong nCount = ReleaseYieldMutex();
816         osl_waitCondition( maWaitingYieldCond, &aVal );
817         AcquireYieldMutex( nCount );
818     }
819 
820 	// we get some apple events way too early
821 	// before the application is ready to handle them,
822 	// so their corresponding application events need to be delayed
823 	// now is a good time to handle at least one of them
824 	if( bWait && !aAppEventList.empty() && ImplGetSVData()->maAppData.mbInAppExecute )
825 	{
826 		// make sure that only one application event is active at a time
827 		static bool bInAppEvent = false;
828 		if( !bInAppEvent )
829 		{
830 			bInAppEvent = true;
831 			// get the next delayed application event
832 			const ApplicationEvent* pAppEvent = aAppEventList.front();
833 			aAppEventList.pop_front();
834 			// handle one application event (no recursion)
835 			const ImplSVData* pSVData = ImplGetSVData();
836 			pSVData->mpApp->AppEvent( *pAppEvent );
837 			delete pAppEvent;
838 			// allow the next delayed application event
839 			bInAppEvent = false;
840 		}
841 	}
842 }
843 
844 // -----------------------------------------------------------------------
845 
846 bool AquaSalInstance::AnyInput( sal_uInt16 nType )
847 {
848     if( nType & INPUT_APPEVENT )
849     {
850         if( ! aAppEventList.empty() )
851             return true;
852         if( nType == INPUT_APPEVENT )
853             return false;
854     }
855 
856     if( nType & INPUT_TIMER )
857     {
858         if( AquaSalTimer::pRunningTimer )
859         {
860             NSDate* pDt = [AquaSalTimer::pRunningTimer fireDate];
861             if( pDt && [pDt timeIntervalSinceNow] < 0 )
862             {
863                 return true;
864             }
865         }
866     }
867 
868 	unsigned/*NSUInteger*/ nEventMask = 0;
869 	if( nType & INPUT_MOUSE)
870 		nEventMask |=
871 			NSLeftMouseDownMask    | NSRightMouseDownMask    | NSOtherMouseDownMask |
872 			NSLeftMouseUpMask      | NSRightMouseUpMask      | NSOtherMouseUpMask |
873 			NSLeftMouseDraggedMask | NSRightMouseDraggedMask | NSOtherMouseDraggedMask |
874 			NSScrollWheelMask |
875 			// NSMouseMovedMask |
876 			NSMouseEnteredMask | NSMouseExitedMask;
877 	if( nType & INPUT_KEYBOARD)
878 		nEventMask |= NSKeyDownMask | NSKeyUpMask | NSFlagsChangedMask;
879 	if( nType & INPUT_OTHER)
880 		nEventMask |= NSTabletPoint;
881 	// TODO: INPUT_PAINT / more INPUT_OTHER
882 	if( !nType)
883 		return false;
884 
885         NSEvent* pEvent = [NSApp nextEventMatchingMask: nEventMask untilDate: nil
886                             inMode: NSDefaultRunLoopMode dequeue: NO];
887 	return (pEvent != NULL);
888 }
889 
890 // -----------------------------------------------------------------------
891 
892 SalFrame* AquaSalInstance::CreateChildFrame( SystemParentData*, sal_uLong /*nSalFrameStyle*/ )
893 {
894 	return NULL;
895 }
896 
897 // -----------------------------------------------------------------------
898 
899 SalFrame* AquaSalInstance::CreateFrame( SalFrame* pParent, sal_uLong nSalFrameStyle )
900 {
901     SalData::ensureThreadAutoreleasePool();
902 
903     SalFrame* pFrame = new AquaSalFrame( pParent, nSalFrameStyle );
904     return pFrame;
905 }
906 
907 // -----------------------------------------------------------------------
908 
909 void AquaSalInstance::DestroyFrame( SalFrame* pFrame )
910 {
911 	delete pFrame;
912 }
913 
914 // -----------------------------------------------------------------------
915 
916 SalObject* AquaSalInstance::CreateObject( SalFrame* pParent, SystemWindowData* /* pWindowData */, sal_Bool /* bShow */ )
917 {
918     // SystemWindowData is meaningless on Mac OS X
919 	AquaSalObject *pObject = NULL;
920 
921 	if ( pParent )
922 		pObject = new AquaSalObject( static_cast<AquaSalFrame*>(pParent) );
923 
924 	return pObject;
925 }
926 
927 // -----------------------------------------------------------------------
928 
929 void AquaSalInstance::DestroyObject( SalObject* pObject )
930 {
931 	delete ( pObject );
932 }
933 
934 // -----------------------------------------------------------------------
935 
936 SalPrinter* AquaSalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
937 {
938 	return new AquaSalPrinter( dynamic_cast<AquaSalInfoPrinter*>(pInfoPrinter) );
939 }
940 
941 // -----------------------------------------------------------------------
942 
943 void AquaSalInstance::DestroyPrinter( SalPrinter* pPrinter )
944 {
945     delete pPrinter;
946 }
947 
948 // -----------------------------------------------------------------------
949 
950 void AquaSalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList )
951 {
952     NSArray* pNames = [NSPrinter printerNames];
953     NSArray* pTypes = [NSPrinter printerTypes];
954     unsigned int nNameCount = pNames ? [pNames count] : 0;
955     unsigned int nTypeCount = pTypes ? [pTypes count] : 0;
956     DBG_ASSERT( nTypeCount == nNameCount, "type count not equal to printer count" );
957     for( unsigned int i = 0; i < nNameCount; i++ )
958     {
959         NSString* pName = [pNames objectAtIndex: i];
960         NSString* pType = i < nTypeCount ? [pTypes objectAtIndex: i] : nil;
961         if( pName )
962         {
963             SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo;
964             pInfo->maPrinterName    = GetOUString( pName );
965             if( pType )
966                 pInfo->maDriver     = GetOUString( pType );
967             pInfo->mnStatus         = 0;
968             pInfo->mnJobs           = 0;
969             pInfo->mpSysData        = NULL;
970 
971             pList->Add( pInfo );
972         }
973     }
974 }
975 
976 // -----------------------------------------------------------------------
977 
978 void AquaSalInstance::GetPrinterQueueState( SalPrinterQueueInfo* )
979 {
980 }
981 
982 // -----------------------------------------------------------------------
983 
984 void AquaSalInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo )
985 {
986     delete pInfo;
987 }
988 
989 // -----------------------------------------------------------------------
990 
991 XubString AquaSalInstance::GetDefaultPrinter()
992 {
993     // #i113170# may not be the main thread if called from UNO API
994     SalData::ensureThreadAutoreleasePool();
995 
996 	if( ! maDefaultPrinter.getLength() )
997     {
998         NSPrintInfo* pPI = [NSPrintInfo sharedPrintInfo];
999         DBG_ASSERT( pPI, "no print info" );
1000         if( pPI )
1001         {
1002             NSPrinter* pPr = [pPI printer];
1003             DBG_ASSERT( pPr, "no printer in default info" );
1004             if( pPr )
1005             {
1006                 NSString* pDefName = [pPr name];
1007                 DBG_ASSERT( pDefName, "printer has no name" );
1008                 maDefaultPrinter = GetOUString( pDefName );
1009             }
1010         }
1011     }
1012     return maDefaultPrinter;
1013 }
1014 
1015 // -----------------------------------------------------------------------
1016 
1017 SalInfoPrinter* AquaSalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
1018 												ImplJobSetup* pSetupData )
1019 {
1020     // #i113170# may not be the main thread if called from UNO API
1021     SalData::ensureThreadAutoreleasePool();
1022 
1023 	SalInfoPrinter* pNewInfoPrinter = NULL;
1024     if( pQueueInfo )
1025     {
1026         pNewInfoPrinter = new AquaSalInfoPrinter( *pQueueInfo );
1027         if( pSetupData )
1028             pNewInfoPrinter->SetPrinterData( pSetupData );
1029     }
1030 
1031     return pNewInfoPrinter;
1032 }
1033 
1034 // -----------------------------------------------------------------------
1035 
1036 void AquaSalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter )
1037 {
1038     // #i113170# may not be the main thread if called from UNO API
1039     SalData::ensureThreadAutoreleasePool();
1040 
1041     delete pPrinter;
1042 }
1043 
1044 // -----------------------------------------------------------------------
1045 
1046 SalSystem* AquaSalInstance::CreateSystem()
1047 {
1048 	return new AquaSalSystem();
1049 }
1050 
1051 // -----------------------------------------------------------------------
1052 
1053 void AquaSalInstance::DestroySystem( SalSystem* pSystem )
1054 {
1055 	delete pSystem;
1056 }
1057 
1058 // -----------------------------------------------------------------------
1059 
1060 void AquaSalInstance::SetEventCallback( void*, bool(*)(void*,void*,int) )
1061 {
1062 }
1063 
1064 // -----------------------------------------------------------------------
1065 
1066 void AquaSalInstance::SetErrorEventCallback( void*, bool(*)(void*,void*,int) )
1067 {
1068 }
1069 
1070 // -----------------------------------------------------------------------
1071 
1072 void* AquaSalInstance::GetConnectionIdentifier( ConnectionIdentifierType& rReturnedType, int& rReturnedBytes )
1073 {
1074 	rReturnedBytes	= 1;
1075 	rReturnedType	= AsciiCString;
1076 	return (void*)"";
1077 }
1078 
1079 // We need to re-encode file urls because osl_getFileURLFromSystemPath converts
1080 // to UTF-8 before encoding non ascii characters, which is not what other apps expect.
1081 static rtl::OUString translateToExternalUrl(const rtl::OUString& internalUrl)
1082 {
1083     rtl::OUString extUrl;
1084 
1085     uno::Reference< lang::XMultiServiceFactory > sm = comphelper::getProcessServiceFactory();
1086     if (sm.is())
1087     {
1088         uno::Reference< beans::XPropertySet > pset;
1089         sm->queryInterface( getCppuType( &pset )) >>= pset;
1090         if (pset.is())
1091         {
1092             uno::Reference< uno::XComponentContext > context;
1093             static const rtl::OUString DEFAULT_CONTEXT( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ) );
1094             pset->getPropertyValue(DEFAULT_CONTEXT) >>= context;
1095             if (context.is())
1096                 extUrl = uri::ExternalUriReferenceTranslator::create(context)->translateToExternal(internalUrl);
1097         }
1098     }
1099     return extUrl;
1100 }
1101 
1102 // #i104525# many versions of OSX have problems with some URLs:
1103 // when an app requests OSX to add one of these URLs to the "Recent Items" list
1104 // then this app gets killed (TextEdit, Preview, etc. and also OOo)
1105 static bool isDangerousUrl( const rtl::OUString& rUrl )
1106 {
1107 	// use a heuristic that detects all known cases since there is no official comment
1108 	// on the exact impact and root cause of the OSX bug
1109 	const int nLen = rUrl.getLength();
1110 	const sal_Unicode* p = rUrl.getStr();
1111 	for( int i = 0; i < nLen-3; ++i, ++p ) {
1112 		if( p[0] != '%' )
1113 			continue;
1114 		// escaped percent?
1115 		if( (p[1] == '2') && (p[2] == '5') )
1116 			return true;
1117 		// escapes are considered to be UTF-8 encoded
1118 		// => check for invalid UTF-8 leading byte
1119 		if( (p[1] != 'f') && (p[1] != 'F') )
1120 			continue;
1121 		int cLowNibble = p[2];
1122 		if( (cLowNibble >= '0' ) && (cLowNibble <= '9'))
1123 			return false;
1124 		if( cLowNibble >= 'a' )
1125 			cLowNibble -= 'a' - 'A';
1126 		if( (cLowNibble < 'A') || (cLowNibble >= 'C'))
1127 			return true;
1128 	}
1129 
1130 	return false;
1131 }
1132 
1133 void AquaSalInstance::AddToRecentDocumentList(const rtl::OUString& rFileUrl, const rtl::OUString& /*rMimeType*/)
1134 {
1135     // Convert file URL for external use (see above)
1136     rtl::OUString externalUrl = translateToExternalUrl(rFileUrl);
1137     if( 0 == externalUrl.getLength() )
1138         externalUrl = rFileUrl;
1139 
1140     if( externalUrl.getLength() && !isDangerousUrl( externalUrl ) )
1141     {
1142         NSString* pString = CreateNSString( externalUrl );
1143         NSURL* pURL = [NSURL URLWithString: pString];
1144 
1145         if( pURL )
1146         {
1147             NSDocumentController* pCtrl = [NSDocumentController sharedDocumentController];
1148             [pCtrl noteNewRecentDocumentURL: pURL];
1149         }
1150         if( pString )
1151             [pString release];
1152     }
1153 }
1154 
1155 
1156 // -----------------------------------------------------------------------
1157 
1158 SalTimer* AquaSalInstance::CreateSalTimer()
1159 {
1160     return new AquaSalTimer();
1161 }
1162 
1163 // -----------------------------------------------------------------------
1164 
1165 SalSystem* AquaSalInstance::CreateSalSystem()
1166 {
1167     return new AquaSalSystem();
1168 }
1169 
1170 // -----------------------------------------------------------------------
1171 
1172 SalBitmap* AquaSalInstance::CreateSalBitmap()
1173 {
1174     return new AquaSalBitmap();
1175 }
1176 
1177 // -----------------------------------------------------------------------
1178 
1179 SalSession* AquaSalInstance::CreateSalSession()
1180 {
1181     return NULL;
1182 }
1183 
1184 // -----------------------------------------------------------------------
1185 
1186 class MacImeStatus : public SalI18NImeStatus
1187 {
1188 public:
1189     MacImeStatus() {}
1190     virtual ~MacImeStatus() {}
1191 
1192     // asks whether there is a status window available
1193     // to toggle into menubar
1194     virtual bool canToggle() { return false; }
1195     virtual void toggle() {}
1196 };
1197 
1198 // -----------------------------------------------------------------------
1199 
1200 SalI18NImeStatus* AquaSalInstance::CreateI18NImeStatus()
1201 {
1202     return new MacImeStatus();
1203 }
1204 
1205 // YieldMutexReleaser
1206 YieldMutexReleaser::YieldMutexReleaser() : mnCount( 0 )
1207 {
1208     SalData* pSalData = GetSalData();
1209     if( ! pSalData->mpFirstInstance->isNSAppThread() )
1210     {
1211         SalData::ensureThreadAutoreleasePool();
1212         mnCount = pSalData->mpFirstInstance->ReleaseYieldMutex();
1213     }
1214 }
1215 
1216 YieldMutexReleaser::~YieldMutexReleaser()
1217 {
1218     if( mnCount != 0 )
1219         GetSalData()->mpFirstInstance->AcquireYieldMutex( mnCount );
1220 }
1221 
1222 //////////////////////////////////////////////////////////////
1223 rtl::OUString GetOUString( CFStringRef rStr )
1224 {
1225     if( rStr == 0 )
1226         return rtl::OUString();
1227     CFIndex nLength = CFStringGetLength( rStr );
1228     if( nLength == 0 )
1229         return rtl::OUString();
1230     const UniChar* pConstStr = CFStringGetCharactersPtr( rStr );
1231     if( pConstStr )
1232         return rtl::OUString( pConstStr, nLength );
1233     UniChar* pStr = reinterpret_cast<UniChar*>( rtl_allocateMemory( sizeof(UniChar)*nLength ) );
1234     CFRange aRange = { 0, nLength };
1235     CFStringGetCharacters( rStr, aRange, pStr );
1236     rtl::OUString aRet( pStr, nLength );
1237     rtl_freeMemory( pStr );
1238     return aRet;
1239 }
1240 
1241 rtl::OUString GetOUString( NSString* pStr )
1242 {
1243     if( ! pStr )
1244         return rtl::OUString();
1245     int nLen = [pStr length];
1246     if( nLen == 0 )
1247         return rtl::OUString();
1248 
1249     rtl::OUStringBuffer aBuf( nLen+1 );
1250     aBuf.setLength( nLen );
1251     [pStr getCharacters: const_cast<sal_Unicode*>(aBuf.getStr())];
1252     return aBuf.makeStringAndClear();
1253 }
1254 
1255 CFStringRef CreateCFString( const rtl::OUString& rStr )
1256 {
1257     return CFStringCreateWithCharacters(kCFAllocatorDefault, rStr.getStr(), rStr.getLength() );
1258 }
1259 
1260 NSString* CreateNSString( const rtl::OUString& rStr )
1261 {
1262     return [[NSString alloc] initWithCharacters: rStr.getStr() length: rStr.getLength()];
1263 }
1264 
1265 CGImageRef CreateCGImage( const Image& rImage )
1266 {
1267     BitmapEx aBmpEx( rImage.GetBitmapEx() );
1268     Bitmap aBmp( aBmpEx.GetBitmap() );
1269 
1270     if( ! aBmp || ! aBmp.ImplGetImpBitmap() )
1271         return NULL;
1272 
1273     // simple case, no transparency
1274     AquaSalBitmap* pSalBmp = static_cast<AquaSalBitmap*>(aBmp.ImplGetImpBitmap()->ImplGetSalBitmap());
1275 
1276     if( ! pSalBmp )
1277         return NULL;
1278 
1279     CGImageRef xImage = NULL;
1280     if( ! (aBmpEx.IsAlpha() || aBmpEx.IsTransparent() ) )
1281         xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
1282     else if( aBmpEx.IsAlpha() )
1283     {
1284         AlphaMask aAlphaMask( aBmpEx.GetAlpha() );
1285         Bitmap aMask( aAlphaMask.GetBitmap() );
1286         AquaSalBitmap* pMaskBmp = static_cast<AquaSalBitmap*>(aMask.ImplGetImpBitmap()->ImplGetSalBitmap());
1287         if( pMaskBmp )
1288             xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
1289         else
1290             xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
1291     }
1292     else if( aBmpEx.GetTransparentType() == TRANSPARENT_BITMAP )
1293     {
1294         Bitmap aMask( aBmpEx.GetMask() );
1295         AquaSalBitmap* pMaskBmp = static_cast<AquaSalBitmap*>(aMask.ImplGetImpBitmap()->ImplGetSalBitmap());
1296         if( pMaskBmp )
1297             xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
1298         else
1299             xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
1300     }
1301     else if( aBmpEx.GetTransparentType() == TRANSPARENT_COLOR )
1302     {
1303         Color aTransColor( aBmpEx.GetTransparentColor() );
1304         SalColor nTransColor = MAKE_SALCOLOR( aTransColor.GetRed(), aTransColor.GetGreen(), aTransColor.GetBlue() );
1305         xImage = pSalBmp->CreateColorMask( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight, nTransColor );
1306     }
1307 
1308     return xImage;
1309 }
1310 
1311 NSImage* CreateNSImage( const Image& rImage )
1312 {
1313     CGImageRef xImage = CreateCGImage( rImage );
1314 
1315     if( ! xImage )
1316         return nil;
1317 
1318     Size aSize( rImage.GetSizePixel() );
1319     NSImage* pImage = [[NSImage alloc] initWithSize: NSMakeSize( aSize.Width(), aSize.Height() )];
1320     if( pImage )
1321     {
1322         [pImage setFlipped: YES];
1323         [pImage lockFocus];
1324 
1325         NSGraphicsContext* pContext = [NSGraphicsContext currentContext];
1326         CGContextRef rCGContext = reinterpret_cast<CGContextRef>([pContext graphicsPort]);
1327 
1328         const CGRect aDstRect = CGRectMake( 0, 0, aSize.Width(), aSize.Height());
1329         CGContextDrawImage( rCGContext, aDstRect, xImage );
1330 
1331         [pImage unlockFocus];
1332     }
1333 
1334     CGImageRelease( xImage );
1335 
1336     return pImage;
1337 }
1338