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