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