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