diff options
Diffstat (limited to 'vcl/aqua/source/app/salinst.cxx')
-rw-r--r-- | vcl/aqua/source/app/salinst.cxx | 1342 |
1 files changed, 1342 insertions, 0 deletions
diff --git a/vcl/aqua/source/app/salinst.cxx b/vcl/aqua/source/app/salinst.cxx new file mode 100644 index 000000000000..7a07718efaac --- /dev/null +++ b/vcl/aqua/source/app/salinst.cxx @@ -0,0 +1,1342 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdio.h> + +#include "tools/fsys.hxx" +#include "tools/getprocessworkingdir.hxx" +#include <tools/solarmutex.hxx> + +#include "osl/process.h" + +#include "rtl/ustrbuf.hxx" + +#include "vcl/svapp.hxx" +#include "vcl/window.hxx" +#include "vcl/timer.hxx" + +#include "aqua/saldata.hxx" +#include "aqua/salinst.h" +#include "aqua/salframe.h" +#include "aqua/salobj.h" +#include "aqua/salsys.h" +#include "aqua/salvd.h" +#include "aqua/salbmp.h" +#include "aqua/salprn.h" +#include "aqua/saltimer.h" +#include "aqua/vclnsapp.h" + +#include "print.h" +#include "impbmp.hxx" +#include "salimestatus.hxx" + +#include <comphelper/processfactory.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/uri/XExternalUriReferenceTranslator.hpp> +#include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include "premac.h" +#include <Foundation/Foundation.h> +#include <ApplicationServices/ApplicationServices.h> +#import "apple_remote/RemoteMainController.h" +#include "apple_remote/RemoteControl.h" +#include "postmac.h" + +using namespace std; +using namespace ::com::sun::star; + +extern sal_Bool ImplSVMain(); + +static sal_Bool* gpbInit = 0; +static NSMenu* pDockMenu = nil; +static bool bNoSVMain = true; +static bool bLeftMain = false; +// ----------------------------------------------------------------------- + +class AquaDelayedSettingsChanged : public Timer +{ + bool mbInvalidate; + public: + AquaDelayedSettingsChanged( bool bInvalidate ) : + mbInvalidate( bInvalidate ) + { + } + + virtual void Timeout() + { + SalData* pSalData = GetSalData(); + if( ! pSalData->maFrames.empty() ) + pSalData->maFrames.front()->CallCallback( SALEVENT_SETTINGSCHANGED, NULL ); + + if( mbInvalidate ) + { + for( std::list< AquaSalFrame* >::iterator it = pSalData->maFrames.begin(); + it != pSalData->maFrames.end(); ++it ) + { + if( (*it)->mbShown ) + (*it)->SendPaintEvent( NULL ); + } + } + Stop(); + delete this; + } +}; + +void AquaSalInstance::delayedSettingsChanged( bool bInvalidate ) +{ + vos::OGuard aGuard( *mpSalYieldMutex ); + AquaDelayedSettingsChanged* pTimer = new AquaDelayedSettingsChanged( bInvalidate ); + pTimer->SetTimeout( 50 ); + pTimer->Start(); +} + + +// the AppEventList must be available before any SalData/SalInst/etc. objects are ready +typedef std::list<const ApplicationEvent*> AppEventList; +AppEventList AquaSalInstance::aAppEventList; + +NSMenu* AquaSalInstance::GetDynamicDockMenu() +{ + if( ! pDockMenu && ! bLeftMain ) + pDockMenu = [[NSMenu alloc] initWithTitle: @""]; + return pDockMenu; +} + +bool AquaSalInstance::isOnCommandLine( const rtl::OUString& rArg ) +{ + sal_uInt32 nArgs = osl_getCommandArgCount(); + for( sal_uInt32 i = 0; i < nArgs; i++ ) + { + rtl::OUString aArg; + osl_getCommandArg( i, &aArg.pData ); + if( aArg.equals( rArg ) ) + return true; + } + return false; +} + + +// initialize the cocoa VCL_NSApplication object +// returns an NSAutoreleasePool that must be released when the event loop begins +static void initNSApp() +{ + // create our cocoa NSApplication + [VCL_NSApplication sharedApplication]; + + SalData::ensureThreadAutoreleasePool(); + + // put cocoa into multithreaded mode + [NSThread detachNewThreadSelector:@selector(enableCocoaThreads:) toTarget:[[CocoaThreadEnabler alloc] init] withObject:nil]; + + // activate our delegate methods + [NSApp setDelegate: NSApp]; + + [[NSNotificationCenter defaultCenter] addObserver: NSApp + selector: @selector(systemColorsChanged:) + name: NSSystemColorsDidChangeNotification + object: nil ]; + [[NSNotificationCenter defaultCenter] addObserver: NSApp + selector: @selector(screenParametersChanged:) + name: NSApplicationDidChangeScreenParametersNotification + object: nil ]; + // add observers for some settings changes that affect vcl's settings + // scrollbar variant + [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp + selector: @selector(scrollbarVariantChanged:) + name: @"AppleAquaScrollBarVariantChanged" + object: nil ]; + // scrollbar page behavior ("jump to here" or not) + [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp + selector: @selector(scrollbarSettingsChanged:) + name: @"AppleNoRedisplayAppearancePreferenceChanged" + object: nil ]; + + // get System Version and store the value in GetSalData()->mnSystemVersion + OSErr err = noErr; + SInt32 systemVersion = VER_TIGER; // Initialize with minimal requirement + if( (err = Gestalt(gestaltSystemVersion, &systemVersion)) == noErr ) + { + GetSalData()->mnSystemVersion = systemVersion; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "System Version %x\n", (unsigned int)systemVersion); +#endif + } + else + NSLog(@"Unable to obtain system version: %ld", (long)err); + + // Initialize Apple Remote + GetSalData()->mpMainController = [[MainController alloc] init]; + + [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp + selector: @selector(applicationWillBecomeActive:) + name: @"AppleRemoteWillBecomeActive" + object: nil ]; + + [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp + selector: @selector(applicationWillResignActive:) + name: @"AppleRemoteWillResignActive" + object: nil ]; + + if( ImplGetSVData()->mbIsTestTool ) + [NSApp activateIgnoringOtherApps: YES]; +} + +sal_Bool ImplSVMainHook( sal_Bool * pbInit ) +{ + gpbInit = pbInit; + + bNoSVMain = false; + initNSApp(); + + NSPoint aPt = { 0, 0 }; + NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined + location: aPt + modifierFlags: 0 + timestamp: 0 + windowNumber: 0 + context: nil + subtype: AquaSalInstance::AppExecuteSVMain + data1: 0 + data2: 0 ]; + if( pEvent ) + { + [NSApp postEvent: pEvent atStart: NO]; + + rtl::OUString aExeURL, aExe; + osl_getExecutableFile( &aExeURL.pData ); + osl_getSystemPathFromFileURL( aExeURL.pData, &aExe.pData ); + rtl::OString aByteExe( rtl::OUStringToOString( aExe, osl_getThreadTextEncoding() ) ); + +#ifdef DEBUG + aByteExe += OString ( " NSAccessibilityDebugLogLevel 1" ); + const char* pArgv[] = { aByteExe.getStr(), NULL }; + NSApplicationMain( 3, pArgv ); +#else + const char* pArgv[] = { aByteExe.getStr(), NULL }; + NSApplicationMain( 1, pArgv ); +#endif + } + else + { + DBG_ERROR( "NSApplication initialization could not be done" ); + } + + return TRUE; // indicate that ImplSVMainHook is implemented +} + +// ======================================================================= + +void SalAbort( const XubString& rErrorText ) +{ + if( !rErrorText.Len() ) + fprintf( stderr, "Application Error " ); + else + fprintf( stderr, "%s ", + ByteString( rErrorText, gsl_getSystemTextEncoding() ).GetBuffer() ); + abort(); +} + +// ----------------------------------------------------------------------- + +void InitSalData() +{ + SalData *pSalData = new SalData; + SetSalData( pSalData ); +} + +// ----------------------------------------------------------------------- + +const ::rtl::OUString& SalGetDesktopEnvironment() +{ + static OUString aDesktopEnvironment(RTL_CONSTASCII_USTRINGPARAM( "MacOSX" )); + return aDesktopEnvironment; +} + +// ----------------------------------------------------------------------- + +void DeInitSalData() +{ + SalData *pSalData = GetSalData(); + if( pSalData->mpStatusItem ) + { + [pSalData->mpStatusItem release]; + pSalData->mpStatusItem = nil; + } + delete pSalData; + SetSalData( NULL ); +} + +// ----------------------------------------------------------------------- + +extern "C" { +#include <crt_externs.h> +} + +// ----------------------------------------------------------------------- + +void InitSalMain() +{ + rtl::OUString urlWorkDir; + rtl_uString *sysWorkDir = NULL; + if (tools::getProcessWorkingDir(&urlWorkDir)) + { + oslFileError err2 = osl_getSystemPathFromFileURL(urlWorkDir.pData, &sysWorkDir); + if (err2 == osl_File_E_None) + { + ByteString aPath( getenv( "PATH" ) ); + ByteString aResPath( getenv( "STAR_RESOURCEPATH" ) ); + ByteString aLibPath( getenv( "DYLD_LIBRARY_PATH" ) ); + ByteString aCmdPath( OUStringToOString(OUString(sysWorkDir), RTL_TEXTENCODING_UTF8).getStr() ); + ByteString aTmpPath; + // Get absolute path of command's directory + if ( aCmdPath.Len() ) { + DirEntry aCmdDirEntry( aCmdPath ); + aCmdDirEntry.ToAbs(); + aCmdPath = ByteString( aCmdDirEntry.GetPath().GetFull(), RTL_TEXTENCODING_ASCII_US ); + } + // Assign to PATH environment variable + if ( aCmdPath.Len() ) + { + aTmpPath = ByteString( "PATH=" ); + aTmpPath += aCmdPath; + if ( aPath.Len() ) + aTmpPath += ByteString( DirEntry::GetSearchDelimiter(), RTL_TEXTENCODING_ASCII_US ); + aTmpPath += aPath; + putenv( (char*)aTmpPath.GetBuffer() ); + } + // Assign to STAR_RESOURCEPATH environment variable + if ( aCmdPath.Len() ) + { + aTmpPath = ByteString( "STAR_RESOURCEPATH=" ); + aTmpPath += aCmdPath; + if ( aResPath.Len() ) + aTmpPath += ByteString( DirEntry::GetSearchDelimiter(), RTL_TEXTENCODING_ASCII_US ); + aTmpPath += aResPath; + putenv( (char*)aTmpPath.GetBuffer() ); + } + // Assign to DYLD_LIBRARY_PATH environment variable + if ( aCmdPath.Len() ) + { + aTmpPath = ByteString( "DYLD_LIBRARY_PATH=" ); + aTmpPath += aCmdPath; + if ( aLibPath.Len() ) + aTmpPath += ByteString( DirEntry::GetSearchDelimiter(), RTL_TEXTENCODING_ASCII_US ); + aTmpPath += aLibPath; + putenv( (char*)aTmpPath.GetBuffer() ); + } + } + } +} + +// ----------------------------------------------------------------------- + +void DeInitSalMain() +{ +} + +// ======================================================================= + +SalYieldMutex::SalYieldMutex() +{ + mnCount = 0; + mnThreadId = 0; +} + +void SalYieldMutex::acquire() +{ + OMutex::acquire(); + mnThreadId = vos::OThread::getCurrentIdentifier(); + mnCount++; +} + +void SalYieldMutex::release() +{ + if ( mnThreadId == vos::OThread::getCurrentIdentifier() ) + { + if ( mnCount == 1 ) + mnThreadId = 0; + mnCount--; + } + OMutex::release(); +} + +sal_Bool SalYieldMutex::tryToAcquire() +{ + if ( OMutex::tryToAcquire() ) + { + mnThreadId = vos::OThread::getCurrentIdentifier(); + mnCount++; + return sal_True; + } + else + return sal_False; +} + +// ----------------------------------------------------------------------- + +// some convenience functions regarding the yield mutex, aka solar mutex + +sal_Bool ImplSalYieldMutexTryToAcquire() +{ + AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance; + if ( pInst ) + return pInst->mpSalYieldMutex->tryToAcquire(); + else + return FALSE; +} + +void ImplSalYieldMutexAcquire() +{ + AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance; + if ( pInst ) + pInst->mpSalYieldMutex->acquire(); +} + +void ImplSalYieldMutexRelease() +{ + AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance; + if ( pInst ) + pInst->mpSalYieldMutex->release(); +} + +// ======================================================================= + +SalInstance* CreateSalInstance() +{ + // this is the case for not using SVMain + // not so good + if( bNoSVMain ) + initNSApp(); + + SalData* pSalData = GetSalData(); + DBG_ASSERT( pSalData->mpFirstInstance == NULL, "more than one instance created" ); + AquaSalInstance* pInst = new AquaSalInstance; + + // init instance (only one instance in this version !!!) + pSalData->mpFirstInstance = pInst; + // this one is for outside AquaSalInstance::Yield + SalData::ensureThreadAutoreleasePool(); + // no focus rects on NWF aqua + ImplGetSVData()->maNWFData.mbNoFocusRects = true; + ImplGetSVData()->maNWFData.mbNoBoldTabFocus = true; + ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise = true; + ImplGetSVData()->maNWFData.mbCenteredTabs = true; + ImplGetSVData()->maNWFData.mbProgressNeedsErase = true; + ImplGetSVData()->maNWFData.mbCheckBoxNeedsErase = true; + ImplGetSVData()->maNWFData.mnStatusBarLowerRightOffset = 10; + ImplGetSVData()->maGDIData.mbNoXORClipping = true; + ImplGetSVData()->maWinData.mbNoSaveBackground = true; + + return pInst; +} + +// ----------------------------------------------------------------------- + +void DestroySalInstance( SalInstance* pInst ) +{ + delete pInst; +} + +// ----------------------------------------------------------------------- + +AquaSalInstance::AquaSalInstance() +{ + mpSalYieldMutex = new SalYieldMutex; + mpSalYieldMutex->acquire(); + ::tools::SolarMutex::SetSolarMutex( mpSalYieldMutex ); + maMainThread = vos::OThread::getCurrentIdentifier(); + mbWaitingYield = false; + maUserEventListMutex = osl_createMutex(); + mnActivePrintJobs = 0; + maWaitingYieldCond = osl_createCondition(); +} + +// ----------------------------------------------------------------------- + +AquaSalInstance::~AquaSalInstance() +{ + ::tools::SolarMutex::SetSolarMutex( 0 ); + mpSalYieldMutex->release(); + delete mpSalYieldMutex; + osl_destroyMutex( maUserEventListMutex ); + osl_destroyCondition( maWaitingYieldCond ); +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::wakeupYield() +{ + // wakeup :Yield + if( mbWaitingYield ) + { + SalData::ensureThreadAutoreleasePool(); + NSPoint aPt = { 0, 0 }; + NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined + location: aPt + modifierFlags: 0 + timestamp: 0 + windowNumber: 0 + context: nil + subtype: AquaSalInstance::YieldWakeupEvent + data1: 0 + data2: 0 ]; + if( pEvent ) + [NSApp postEvent: pEvent atStart: NO]; + } +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::PostUserEvent( AquaSalFrame* pFrame, sal_uInt16 nType, void* pData ) +{ + osl_acquireMutex( maUserEventListMutex ); + maUserEvents.push_back( SalUserEvent( pFrame, pData, nType ) ); + osl_releaseMutex( maUserEventListMutex ); + + // notify main loop that an event has arrived + wakeupYield(); +} + +// ----------------------------------------------------------------------- + +vos::IMutex* AquaSalInstance::GetYieldMutex() +{ + return mpSalYieldMutex; +} + +// ----------------------------------------------------------------------- + +sal_uLong AquaSalInstance::ReleaseYieldMutex() +{ + SalYieldMutex* pYieldMutex = mpSalYieldMutex; + if ( pYieldMutex->GetThreadId() == + vos::OThread::getCurrentIdentifier() ) + { + sal_uLong nCount = pYieldMutex->GetAcquireCount(); + sal_uLong n = nCount; + while ( n ) + { + pYieldMutex->release(); + n--; + } + + return nCount; + } + else + return 0; +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::AcquireYieldMutex( sal_uLong nCount ) +{ + SalYieldMutex* pYieldMutex = mpSalYieldMutex; + while ( nCount ) + { + pYieldMutex->acquire(); + nCount--; + } +} + +// ----------------------------------------------------------------------- + +bool AquaSalInstance::CheckYieldMutex() +{ + bool bRet = true; + + SalYieldMutex* pYieldMutex = mpSalYieldMutex; + if ( pYieldMutex->GetThreadId() != + vos::OThread::getCurrentIdentifier() ) + { + bRet = false; + } + + return bRet; +} + +// ----------------------------------------------------------------------- + +bool AquaSalInstance::isNSAppThread() const +{ + return vos::OThread::getCurrentIdentifier() == maMainThread; +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent ) +{ + switch( [pEvent subtype] ) + { + case AppStartTimerEvent: + AquaSalTimer::handleStartTimerEvent( pEvent ); + break; + case AppEndLoopEvent: + [NSApp stop: NSApp]; + break; + case AppExecuteSVMain: + { + sal_Bool bResult = ImplSVMain(); + if( gpbInit ) + *gpbInit = bResult; + [NSApp stop: NSApp]; + bLeftMain = true; + if( pDockMenu ) + { + [pDockMenu release]; + pDockMenu = nil; + } + } + break; + case AppleRemoteEvent: + { + sal_Int16 nCommand = 0; + SalData* pSalData = GetSalData(); + bool bIsFullScreenMode = false; + + std::list<AquaSalFrame*>::iterator it = pSalData->maFrames.begin(); + while( (*it) && ( (it != pSalData->maFrames.end() ) || ( (*it)->mbFullScreen == false ) ) ) + { + if ( ((*it)->mbFullScreen == true) ) + bIsFullScreenMode = true; + it++; + } + + switch ([pEvent data1]) + { + case kRemoteButtonPlay: + nCommand = ( bIsFullScreenMode == true ) ? MEDIA_COMMAND_PLAY_PAUSE : MEDIA_COMMAND_PLAY; + break; + + // kept for experimentation purpose (scheduled for future implementation) + // case kRemoteButtonMenu: nCommand = MEDIA_COMMAND_MENU; break; + + case kRemoteButtonPlus: nCommand = MEDIA_COMMAND_VOLUME_UP; break; + + case kRemoteButtonMinus: nCommand = MEDIA_COMMAND_VOLUME_DOWN; break; + + case kRemoteButtonRight: nCommand = MEDIA_COMMAND_NEXTTRACK; break; + + case kRemoteButtonRight_Hold: nCommand = MEDIA_COMMAND_NEXTTRACK_HOLD; break; + + case kRemoteButtonLeft: nCommand = MEDIA_COMMAND_PREVIOUSTRACK; break; + + case kRemoteButtonLeft_Hold: nCommand = MEDIA_COMMAND_REWIND; break; + + case kRemoteButtonPlay_Hold: nCommand = MEDIA_COMMAND_PLAY_HOLD; break; + + case kRemoteButtonMenu_Hold: nCommand = MEDIA_COMMAND_STOP; break; + + // FIXME : not detected + case kRemoteButtonPlus_Hold: + case kRemoteButtonMinus_Hold: + break; + + default: + break; + } + AquaSalFrame* pFrame = pSalData->maFrames.front(); + Window * pWindow = pFrame->GetWindow() ? pSalData->maFrames.front()->GetWindow() : NULL; + + if( pWindow ) + { + const Point aPoint; + CommandEvent aCEvt( aPoint, COMMAND_MEDIA, FALSE, &nCommand ); + NotifyEvent aNCmdEvt( EVENT_COMMAND, pWindow, &aCEvt ); + + if ( !ImplCallPreNotify( aNCmdEvt ) ) + pWindow->Command( aCEvt ); + } + + } + break; + + case YieldWakeupEvent: + // do nothing, fall out of Yield + break; + + default: + DBG_ERROR( "unhandled NSApplicationDefined event" ); + break; + }; +} + +// ----------------------------------------------------------------------- + +class ReleasePoolHolder +{ + NSAutoreleasePool* mpPool; + public: + ReleasePoolHolder() : mpPool( [[NSAutoreleasePool alloc] init] ) {} + ~ReleasePoolHolder() { [mpPool release]; } +}; + +void AquaSalInstance::Yield( bool bWait, bool bHandleAllCurrentEvents ) +{ + // ensure that the per thread autorelease pool is top level and + // will therefore not be destroyed by cocoa implicitly + SalData::ensureThreadAutoreleasePool(); + + // NSAutoreleasePool documentation suggests we should have + // an own pool for each yield level + ReleasePoolHolder aReleasePool; + + // Release all locks so that we don't deadlock when we pull pending + // events from the event queue + bool bDispatchUser = true; + while( bDispatchUser ) + { + sal_uLong nCount = ReleaseYieldMutex(); + + // get one user event + osl_acquireMutex( maUserEventListMutex ); + SalUserEvent aEvent( NULL, NULL, 0 ); + if( ! maUserEvents.empty() ) + { + aEvent = maUserEvents.front(); + maUserEvents.pop_front(); + } + else + bDispatchUser = false; + osl_releaseMutex( maUserEventListMutex ); + + AcquireYieldMutex( nCount ); + + // dispatch it + if( aEvent.mpFrame && AquaSalFrame::isAlive( aEvent.mpFrame ) ) + { + aEvent.mpFrame->CallCallback( aEvent.mnType, aEvent.mpData ); + osl_setCondition( maWaitingYieldCond ); + // return if only one event is asked for + if( ! bHandleAllCurrentEvents ) + return; + } + } + + // handle cocoa event queue + // cocoa events mye be only handled in the thread the NSApp was created + if( isNSAppThread() && mnActivePrintJobs == 0 ) + { + // we need to be woken up by a cocoa-event + // if a user event should be posted by the event handling below + bool bOldWaitingYield = mbWaitingYield; + mbWaitingYield = bWait; + + // handle available events + NSEvent* pEvent = nil; + bool bHadEvent = false; + do + { + sal_uLong nCount = ReleaseYieldMutex(); + + pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: nil + inMode: NSDefaultRunLoopMode dequeue: YES]; + if( pEvent ) + { + [NSApp sendEvent: pEvent]; + bHadEvent = true; + } + [NSApp updateWindows]; + + AcquireYieldMutex( nCount ); + } while( bHandleAllCurrentEvents && pEvent ); + + // if we had no event yet, wait for one if requested + if( bWait && ! bHadEvent ) + { + sal_uLong nCount = ReleaseYieldMutex(); + + NSDate* pDt = AquaSalTimer::pRunningTimer ? [AquaSalTimer::pRunningTimer fireDate] : [NSDate distantFuture]; + pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: pDt + inMode: NSDefaultRunLoopMode dequeue: YES]; + if( pEvent ) + [NSApp sendEvent: pEvent]; + [NSApp updateWindows]; + + AcquireYieldMutex( nCount ); + + // #i86581# + // FIXME: sometimes the NSTimer will never fire. Firing it by hand then + // fixes the problem even seems to set the correct next firing date + // Why oh why ? + if( ! pEvent && AquaSalTimer::pRunningTimer ) + { + // this cause crashes on MacOSX 10.4 + // [AquaSalTimer::pRunningTimer fire]; + ImplGetSVData()->mpSalTimer->CallCallback(); + } + } + + mbWaitingYield = bOldWaitingYield; + + // collect update rectangles + const std::list< AquaSalFrame* > rFrames( GetSalData()->maFrames ); + for( std::list< AquaSalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it ) + { + if( (*it)->mbShown && ! (*it)->maInvalidRect.IsEmpty() ) + { + (*it)->Flush( (*it)->maInvalidRect ); + (*it)->maInvalidRect.SetEmpty(); + } + } + osl_setCondition( maWaitingYieldCond ); + } + else if( bWait ) + { + // #i103162# + // wait until any thread (most likely the main thread) + // has dispatched an event, cop out at 200 ms + osl_resetCondition( maWaitingYieldCond ); + TimeValue aVal = { 0, 200000000 }; + sal_uLong nCount = ReleaseYieldMutex(); + osl_waitCondition( maWaitingYieldCond, &aVal ); + AcquireYieldMutex( nCount ); + } + + // we get some apple events way too early + // before the application is ready to handle them, + // so their corresponding application events need to be delayed + // now is a good time to handle at least one of them + if( bWait && !aAppEventList.empty() && ImplGetSVData()->maAppData.mbInAppExecute ) + { + // make sure that only one application event is active at a time + static bool bInAppEvent = false; + if( !bInAppEvent ) + { + bInAppEvent = true; + // get the next delayed application event + const ApplicationEvent* pAppEvent = aAppEventList.front(); + aAppEventList.pop_front(); + // handle one application event (no recursion) + const ImplSVData* pSVData = ImplGetSVData(); + pSVData->mpApp->AppEvent( *pAppEvent ); + delete pAppEvent; + // allow the next delayed application event + bInAppEvent = false; + } + } +} + +// ----------------------------------------------------------------------- + +bool AquaSalInstance::AnyInput( sal_uInt16 nType ) +{ + if( nType & INPUT_APPEVENT ) + { + if( ! aAppEventList.empty() ) + return true; + if( nType == INPUT_APPEVENT ) + return false; + } + + if( nType & INPUT_TIMER ) + { + if( AquaSalTimer::pRunningTimer ) + { + NSDate* pDt = [AquaSalTimer::pRunningTimer fireDate]; + if( pDt && [pDt timeIntervalSinceNow] < 0 ) + { + return true; + } + } + } + + unsigned/*NSUInteger*/ nEventMask = 0; + if( nType & INPUT_MOUSE) + nEventMask |= + NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask | + NSLeftMouseUpMask | NSRightMouseUpMask | NSOtherMouseUpMask | + NSLeftMouseDraggedMask | NSRightMouseDraggedMask | NSOtherMouseDraggedMask | + NSScrollWheelMask | + // NSMouseMovedMask | + NSMouseEnteredMask | NSMouseExitedMask; + if( nType & INPUT_KEYBOARD) + nEventMask |= NSKeyDownMask | NSKeyUpMask | NSFlagsChangedMask; + if( nType & INPUT_OTHER) + nEventMask |= NSTabletPoint; + // TODO: INPUT_PAINT / more INPUT_OTHER + if( !nType) + return false; + + NSEvent* pEvent = [NSApp nextEventMatchingMask: nEventMask untilDate: nil + inMode: NSDefaultRunLoopMode dequeue: NO]; + return (pEvent != NULL); +} + +// ----------------------------------------------------------------------- + +SalFrame* AquaSalInstance::CreateChildFrame( SystemParentData*, sal_uLong /*nSalFrameStyle*/ ) +{ + return NULL; +} + +// ----------------------------------------------------------------------- + +SalFrame* AquaSalInstance::CreateFrame( SalFrame* pParent, sal_uLong nSalFrameStyle ) +{ + SalData::ensureThreadAutoreleasePool(); + + SalFrame* pFrame = new AquaSalFrame( pParent, nSalFrameStyle ); + return pFrame; +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::DestroyFrame( SalFrame* pFrame ) +{ + delete pFrame; +} + +// ----------------------------------------------------------------------- + +SalObject* AquaSalInstance::CreateObject( SalFrame* pParent, SystemWindowData* /* pWindowData */, sal_Bool /* bShow */ ) +{ + // SystemWindowData is meaningless on Mac OS X + AquaSalObject *pObject = NULL; + + if ( pParent ) + pObject = new AquaSalObject( static_cast<AquaSalFrame*>(pParent) ); + + return pObject; +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::DestroyObject( SalObject* pObject ) +{ + delete ( pObject ); +} + +// ----------------------------------------------------------------------- + +SalPrinter* AquaSalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter ) +{ + return new AquaSalPrinter( dynamic_cast<AquaSalInfoPrinter*>(pInfoPrinter) ); +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::DestroyPrinter( SalPrinter* pPrinter ) +{ + delete pPrinter; +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList ) +{ + NSArray* pNames = [NSPrinter printerNames]; + NSArray* pTypes = [NSPrinter printerTypes]; + unsigned int nNameCount = pNames ? [pNames count] : 0; + unsigned int nTypeCount = pTypes ? [pTypes count] : 0; + DBG_ASSERT( nTypeCount == nNameCount, "type count not equal to printer count" ); + for( unsigned int i = 0; i < nNameCount; i++ ) + { + NSString* pName = [pNames objectAtIndex: i]; + NSString* pType = i < nTypeCount ? [pTypes objectAtIndex: i] : nil; + if( pName ) + { + SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo; + pInfo->maPrinterName = GetOUString( pName ); + if( pType ) + pInfo->maDriver = GetOUString( pType ); + pInfo->mnStatus = 0; + pInfo->mnJobs = 0; + pInfo->mpSysData = NULL; + + pList->Add( pInfo ); + } + } +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::GetPrinterQueueState( SalPrinterQueueInfo* ) +{ +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo ) +{ + delete pInfo; +} + +// ----------------------------------------------------------------------- + +XubString AquaSalInstance::GetDefaultPrinter() +{ + // #i113170# may not be the main thread if called from UNO API + SalData::ensureThreadAutoreleasePool(); + + if( ! maDefaultPrinter.getLength() ) + { + NSPrintInfo* pPI = [NSPrintInfo sharedPrintInfo]; + DBG_ASSERT( pPI, "no print info" ); + if( pPI ) + { + NSPrinter* pPr = [pPI printer]; + DBG_ASSERT( pPr, "no printer in default info" ); + if( pPr ) + { + NSString* pDefName = [pPr name]; + DBG_ASSERT( pDefName, "printer has no name" ); + maDefaultPrinter = GetOUString( pDefName ); + } + } + } + return maDefaultPrinter; +} + +// ----------------------------------------------------------------------- + +SalInfoPrinter* AquaSalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo, + ImplJobSetup* pSetupData ) +{ + // #i113170# may not be the main thread if called from UNO API + SalData::ensureThreadAutoreleasePool(); + + SalInfoPrinter* pNewInfoPrinter = NULL; + if( pQueueInfo ) + { + pNewInfoPrinter = new AquaSalInfoPrinter( *pQueueInfo ); + if( pSetupData ) + pNewInfoPrinter->SetPrinterData( pSetupData ); + } + + return pNewInfoPrinter; +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter ) +{ + // #i113170# may not be the main thread if called from UNO API + SalData::ensureThreadAutoreleasePool(); + + delete pPrinter; +} + +// ----------------------------------------------------------------------- + +SalSystem* AquaSalInstance::CreateSystem() +{ + return new AquaSalSystem(); +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::DestroySystem( SalSystem* pSystem ) +{ + delete pSystem; +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::SetEventCallback( void*, bool(*)(void*,void*,int) ) +{ +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::SetErrorEventCallback( void*, bool(*)(void*,void*,int) ) +{ +} + +// ----------------------------------------------------------------------- + +void* AquaSalInstance::GetConnectionIdentifier( ConnectionIdentifierType& rReturnedType, int& rReturnedBytes ) +{ + rReturnedBytes = 1; + rReturnedType = AsciiCString; + return (void*)""; +} + +// We need to re-encode file urls because osl_getFileURLFromSystemPath converts +// to UTF-8 before encoding non ascii characters, which is not what other apps expect. +static rtl::OUString translateToExternalUrl(const rtl::OUString& internalUrl) +{ + rtl::OUString extUrl; + + uno::Reference< lang::XMultiServiceFactory > sm = comphelper::getProcessServiceFactory(); + if (sm.is()) + { + uno::Reference< beans::XPropertySet > pset; + sm->queryInterface( getCppuType( &pset )) >>= pset; + if (pset.is()) + { + uno::Reference< uno::XComponentContext > context; + static const rtl::OUString DEFAULT_CONTEXT( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ) ); + pset->getPropertyValue(DEFAULT_CONTEXT) >>= context; + if (context.is()) + extUrl = uri::ExternalUriReferenceTranslator::create(context)->translateToExternal(internalUrl); + } + } + return extUrl; +} + +// #i104525# many versions of OSX have problems with some URLs: +// when an app requests OSX to add one of these URLs to the "Recent Items" list +// then this app gets killed (TextEdit, Preview, etc. and also OOo) +static bool isDangerousUrl( const rtl::OUString& rUrl ) +{ + // use a heuristic that detects all known cases since there is no official comment + // on the exact impact and root cause of the OSX bug + const int nLen = rUrl.getLength(); + const sal_Unicode* p = rUrl.getStr(); + for( int i = 0; i < nLen-3; ++i, ++p ) { + if( p[0] != '%' ) + continue; + // escaped percent? + if( (p[1] == '2') && (p[2] == '5') ) + return true; + // escapes are considered to be UTF-8 encoded + // => check for invalid UTF-8 leading byte + if( (p[1] != 'f') && (p[1] != 'F') ) + continue; + int cLowNibble = p[2]; + if( (cLowNibble >= '0' ) && (cLowNibble <= '9')) + return false; + if( cLowNibble >= 'a' ) + cLowNibble -= 'a' - 'A'; + if( (cLowNibble < 'A') || (cLowNibble >= 'C')) + return true; + } + + return false; +} + +void AquaSalInstance::AddToRecentDocumentList(const rtl::OUString& rFileUrl, const rtl::OUString& /*rMimeType*/) +{ + // Convert file URL for external use (see above) + rtl::OUString externalUrl = translateToExternalUrl(rFileUrl); + if( 0 == externalUrl.getLength() ) + externalUrl = rFileUrl; + + if( externalUrl.getLength() && !isDangerousUrl( externalUrl ) ) + { + NSString* pString = CreateNSString( externalUrl ); + NSURL* pURL = [NSURL URLWithString: pString]; + + if( pURL ) + { + NSDocumentController* pCtrl = [NSDocumentController sharedDocumentController]; + [pCtrl noteNewRecentDocumentURL: pURL]; + } + if( pString ) + [pString release]; + } +} + + +// ----------------------------------------------------------------------- + +SalTimer* AquaSalInstance::CreateSalTimer() +{ + return new AquaSalTimer(); +} + +// ----------------------------------------------------------------------- + +SalSystem* AquaSalInstance::CreateSalSystem() +{ + return new AquaSalSystem(); +} + +// ----------------------------------------------------------------------- + +SalBitmap* AquaSalInstance::CreateSalBitmap() +{ + return new AquaSalBitmap(); +} + +// ----------------------------------------------------------------------- + +SalSession* AquaSalInstance::CreateSalSession() +{ + return NULL; +} + +// ----------------------------------------------------------------------- + +class MacImeStatus : public SalI18NImeStatus +{ +public: + MacImeStatus() {} + virtual ~MacImeStatus() {} + + // asks whether there is a status window available + // to toggle into menubar + virtual bool canToggle() { return false; } + virtual void toggle() {} +}; + +// ----------------------------------------------------------------------- + +SalI18NImeStatus* AquaSalInstance::CreateI18NImeStatus() +{ + return new MacImeStatus(); +} + +// YieldMutexReleaser +YieldMutexReleaser::YieldMutexReleaser() : mnCount( 0 ) +{ + SalData* pSalData = GetSalData(); + if( ! pSalData->mpFirstInstance->isNSAppThread() ) + { + SalData::ensureThreadAutoreleasePool(); + mnCount = pSalData->mpFirstInstance->ReleaseYieldMutex(); + } +} + +YieldMutexReleaser::~YieldMutexReleaser() +{ + if( mnCount != 0 ) + GetSalData()->mpFirstInstance->AcquireYieldMutex( mnCount ); +} + +////////////////////////////////////////////////////////////// +rtl::OUString GetOUString( CFStringRef rStr ) +{ + if( rStr == 0 ) + return rtl::OUString(); + CFIndex nLength = CFStringGetLength( rStr ); + if( nLength == 0 ) + return rtl::OUString(); + const UniChar* pConstStr = CFStringGetCharactersPtr( rStr ); + if( pConstStr ) + return rtl::OUString( pConstStr, nLength ); + UniChar* pStr = reinterpret_cast<UniChar*>( rtl_allocateMemory( sizeof(UniChar)*nLength ) ); + CFRange aRange = { 0, nLength }; + CFStringGetCharacters( rStr, aRange, pStr ); + rtl::OUString aRet( pStr, nLength ); + rtl_freeMemory( pStr ); + return aRet; +} + +rtl::OUString GetOUString( NSString* pStr ) +{ + if( ! pStr ) + return rtl::OUString(); + int nLen = [pStr length]; + if( nLen == 0 ) + return rtl::OUString(); + + rtl::OUStringBuffer aBuf( nLen+1 ); + aBuf.setLength( nLen ); + [pStr getCharacters: const_cast<sal_Unicode*>(aBuf.getStr())]; + return aBuf.makeStringAndClear(); +} + +CFStringRef CreateCFString( const rtl::OUString& rStr ) +{ + return CFStringCreateWithCharacters(kCFAllocatorDefault, rStr.getStr(), rStr.getLength() ); +} + +NSString* CreateNSString( const rtl::OUString& rStr ) +{ + return [[NSString alloc] initWithCharacters: rStr.getStr() length: rStr.getLength()]; +} + +CGImageRef CreateCGImage( const Image& rImage ) +{ + BitmapEx aBmpEx( rImage.GetBitmapEx() ); + Bitmap aBmp( aBmpEx.GetBitmap() ); + + if( ! aBmp || ! aBmp.ImplGetImpBitmap() ) + return NULL; + + // simple case, no transparency + AquaSalBitmap* pSalBmp = static_cast<AquaSalBitmap*>(aBmp.ImplGetImpBitmap()->ImplGetSalBitmap()); + + if( ! pSalBmp ) + return NULL; + + CGImageRef xImage = NULL; + if( ! (aBmpEx.IsAlpha() || aBmpEx.IsTransparent() ) ) + xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); + else if( aBmpEx.IsAlpha() ) + { + AlphaMask aAlphaMask( aBmpEx.GetAlpha() ); + Bitmap aMask( aAlphaMask.GetBitmap() ); + AquaSalBitmap* pMaskBmp = static_cast<AquaSalBitmap*>(aMask.ImplGetImpBitmap()->ImplGetSalBitmap()); + if( pMaskBmp ) + xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); + else + xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); + } + else if( aBmpEx.GetTransparentType() == TRANSPARENT_BITMAP ) + { + Bitmap aMask( aBmpEx.GetMask() ); + AquaSalBitmap* pMaskBmp = static_cast<AquaSalBitmap*>(aMask.ImplGetImpBitmap()->ImplGetSalBitmap()); + if( pMaskBmp ) + xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); + else + xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); + } + else if( aBmpEx.GetTransparentType() == TRANSPARENT_COLOR ) + { + Color aTransColor( aBmpEx.GetTransparentColor() ); + SalColor nTransColor = MAKE_SALCOLOR( aTransColor.GetRed(), aTransColor.GetGreen(), aTransColor.GetBlue() ); + xImage = pSalBmp->CreateColorMask( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight, nTransColor ); + } + + return xImage; +} + +NSImage* CreateNSImage( const Image& rImage ) +{ + CGImageRef xImage = CreateCGImage( rImage ); + + if( ! xImage ) + return nil; + + Size aSize( rImage.GetSizePixel() ); + NSImage* pImage = [[NSImage alloc] initWithSize: NSMakeSize( aSize.Width(), aSize.Height() )]; + if( pImage ) + { + [pImage setFlipped: YES]; + [pImage lockFocus]; + + NSGraphicsContext* pContext = [NSGraphicsContext currentContext]; + CGContextRef rCGContext = reinterpret_cast<CGContextRef>([pContext graphicsPort]); + + const CGRect aDstRect = { {0, 0}, { aSize.Width(), aSize.Height() } }; + CGContextDrawImage( rCGContext, aDstRect, xImage ); + + [pImage unlockFocus]; + } + + CGImageRelease( xImage ); + + return pImage; +} |