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