diff options
Diffstat (limited to 'vcl/aqua/source/app')
-rw-r--r-- | vcl/aqua/source/app/makefile.mk | 63 | ||||
-rw-r--r-- | vcl/aqua/source/app/saldata.cxx | 293 | ||||
-rw-r--r-- | vcl/aqua/source/app/salinst.cxx | 1323 | ||||
-rwxr-xr-x | vcl/aqua/source/app/salnstimer.mm | 56 | ||||
-rw-r--r-- | vcl/aqua/source/app/salsys.cxx | 220 | ||||
-rw-r--r-- | vcl/aqua/source/app/saltimer.cxx | 135 | ||||
-rwxr-xr-x | vcl/aqua/source/app/vclnsapp.mm | 518 |
7 files changed, 2608 insertions, 0 deletions
diff --git a/vcl/aqua/source/app/makefile.mk b/vcl/aqua/source/app/makefile.mk new file mode 100644 index 000000000000..a0ddcbc02226 --- /dev/null +++ b/vcl/aqua/source/app/makefile.mk @@ -0,0 +1,63 @@ +#************************************************************************* +# +# 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. +# +#************************************************************************* + +PRJ=..$/..$/.. + +PRJNAME=vcl +TARGET=salapp +.INCLUDE : $(PRJ)$/util$/makefile.pmk +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile2.pmk + +# --- Files -------------------------------------------------------- + +.IF "$(GUIBASE)"!="aqua" + +dummy: + @echo "Nothing to build for GUIBASE $(GUIBASE)" + +.ELSE # "$(GUIBASE)"!="aqua" + +SLOFILES= $(SLO)$/salinst.obj \ + $(SLO)$/saldata.obj \ + $(SLO)$/vclnsapp.obj \ + $(SLO)$/saltimer.obj \ + $(SLO)$/salnstimer.obj \ + $(SLO)$/salsys.obj + +.ENDIF # "$(GUIBASE)"!="aqua" + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + +.INCLUDE : $(PRJ)$/util$/target.pmk + diff --git a/vcl/aqua/source/app/saldata.cxx b/vcl/aqua/source/app/saldata.cxx new file mode 100644 index 000000000000..3cb878636ad3 --- /dev/null +++ b/vcl/aqua/source/app/saldata.cxx @@ -0,0 +1,293 @@ +/************************************************************************* + * + * 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 "saldata.hxx" +#include "salnsmenu.h" +#include "salinst.h" +#import "apple_remote/RemoteMainController.h" + +oslThreadKey SalData::s_aAutoReleaseKey = 0; + +static void SAL_CALL releasePool( void* pPool ) +{ + if( pPool ) + [(NSAutoreleasePool*)pPool release]; +} + +SalData::SalData() +: + mpTimerProc( NULL ), + mpFirstInstance( NULL ), + mpFirstObject( NULL ), + mpFirstVD( NULL ), + mpFirstPrinter( NULL ), + mpFontList( NULL ), + mpStatusItem( nil ), + mxRGBSpace( CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB) ), + mxGraySpace( CGColorSpaceCreateWithName(kCGColorSpaceGenericGray) ), + mxP50Space( NULL ), + mxP50Pattern( NULL ), + maCursors( POINTER_COUNT, INVALID_CURSOR_PTR ), + mbIsScrollbarDoubleMax( false ), + mnSystemVersion( VER_TIGER ), + mpMainController( NULL ), + mpDockIconClickHandler( nil ), + mnDPIX( 0 ), + mnDPIY( 0 ) +{ + if( s_aAutoReleaseKey == 0 ) + s_aAutoReleaseKey = osl_createThreadKey( releasePool ); +} + +SalData::~SalData() +{ + CGPatternRelease( mxP50Pattern ); + CGColorSpaceRelease( mxP50Space ); + CGColorSpaceRelease( mxRGBSpace ); + CGColorSpaceRelease( mxGraySpace ); + for( unsigned int i = 0; i < maCursors.size(); i++ ) + { + NSCursor* pCurs = maCursors[i]; + if( pCurs && pCurs != INVALID_CURSOR_PTR ) + [pCurs release]; + } + if( s_aAutoReleaseKey ) + { + // release the last pool + NSAutoreleasePool* pPool = nil; + pPool = reinterpret_cast<NSAutoreleasePool*>( osl_getThreadKeyData( s_aAutoReleaseKey ) ); + if( pPool ) + { + osl_setThreadKeyData( s_aAutoReleaseKey, NULL ); + [pPool release]; + } + + osl_destroyThreadKey( s_aAutoReleaseKey ); + s_aAutoReleaseKey = 0; + } + if ( mpMainController ) + [mpMainController release]; +} + +void SalData::ensureThreadAutoreleasePool() +{ + NSAutoreleasePool* pPool = nil; + if( s_aAutoReleaseKey ) + { + pPool = reinterpret_cast<NSAutoreleasePool*>( osl_getThreadKeyData( s_aAutoReleaseKey ) ); + if( ! pPool ) + { + pPool = [[NSAutoreleasePool alloc] init]; + osl_setThreadKeyData( s_aAutoReleaseKey, pPool ); + } + } + else + { + DBG_ERROR( "no autorelease key" ); + } +} + +void SalData::drainThreadAutoreleasePool() +{ + NSAutoreleasePool* pPool = nil; + if( s_aAutoReleaseKey ) + { + pPool = reinterpret_cast<NSAutoreleasePool*>( osl_getThreadKeyData( s_aAutoReleaseKey ) ); + if( pPool ) + { + // osl_setThreadKeyData( s_aAutoReleaseKey, NULL ); + // [pPool release]; + [pPool drain]; + } + else + { + pPool = [[NSAutoreleasePool alloc] init]; + osl_setThreadKeyData( s_aAutoReleaseKey, pPool ); + } + } + else + { + DBG_ERROR( "no autorelease key" ); + } +} + + +struct curs_ent +{ + const char* pBaseName; + const NSPoint aHotSpot; +} +const aCursorTab[ POINTER_COUNT ] = +{ +{ NULL, { 0, 0 } }, //POINTER_ARROW +{ "nullptr", { 16, 16 } }, //POINTER_NULL +{ "hourglass", { 15, 15 } }, //POINTER_WAIT +{ NULL, { 0, 0 } }, //POINTER_TEXT +{ "help", { 0, 0 } }, //POINTER_HELP +{ NULL, { 0, 0 } }, //POINTER_CROSS +{ NULL, { 0, 0 } }, //POINTER_MOVE +{ NULL, { 0, 0 } }, //POINTER_NSIZE +{ NULL, { 0, 0 } }, //POINTER_SSIZE +{ NULL, { 0, 0 } }, //POINTER_WSIZE +{ NULL, { 0, 0 } }, //POINTER_ESIZE +{ "nwsesize", { 15, 15 } }, //POINTER_NWSIZE +{ "neswsize", { 15, 15 } }, //POINTER_NESIZE +{ "neswsize", { 15, 15 } }, //POINTER_SWSIZE +{ "nwsesize", { 15, 15 } }, //POINTER_SESIZE +{ NULL, { 0, 0 } }, //POINTER_WINDOW_NSIZE +{ NULL, { 0, 0 } }, //POINTER_WINDOW_SSIZE +{ NULL, { 0, 0 } }, //POINTER_WINDOW_WSIZE +{ NULL, { 0, 0 } }, //POINTER_WINDOW_ESIZE +{ "nwsesize", { 15, 15 } }, //POINTER_WINDOW_NWSIZE +{ "neswsize", { 15, 15 } }, //POINTER_WINDOW_NESIZE +{ "neswsize", { 15, 15 } }, //POINTER_WINDOW_SWSIZE +{ "nwsesize", { 15, 15 } }, //POINTER_WINDOW_SESIZE +{ NULL, { 0, 0 } }, //POINTER_HSPLIT +{ NULL, { 0, 0 } }, //POINTER_VSPLIT +{ NULL, { 0, 0 } }, //POINTER_HSIZEBAR +{ NULL, { 0, 0 } }, //POINTER_VSIZEBAR +{ NULL, { 0, 0 } }, //POINTER_HAND +{ NULL, { 0, 0 } }, //POINTER_REFHAND +{ "pen", { 3, 27 } }, //POINTER_PEN +{ "magnify", { 12, 13 } }, //POINTER_MAGNIFY +{ "fill", { 10, 22 } }, //POINTER_FILL +{ "rotate", { 15, 15 } }, //POINTER_ROTATE +{ "hshear", { 15, 15 } }, //POINTER_HSHEAR +{ "vshear", { 15, 15 } }, //POINTER_VSHEAR +{ "mirror", { 14, 12 } }, //POINTER_MIRROR +{ "crook", { 15, 14 } }, //POINTER_CROOK +{ "crop", { 9, 9 } }, //POINTER_CROP +{ "movept", { 0, 0 } }, //POINTER_MOVEPOINT +{ "movebw", { 0, 0 } }, //POINTER_MOVEBEZIERWEIGHT +{ "movedata", { 0, 0 } }, //POINTER_MOVEDATA +{ "copydata", { 0, 0 } }, //POINTER_COPYDATA +{ "linkdata", { 0, 0 } }, //POINTER_LINKDATA +{ "movedlnk", { 0, 0 } }, //POINTER_MOVEDATALINK +{ "copydlnk", { 0, 0 } }, //POINTER_COPYDATALINK +{ "movef", { 8, 8 } }, //POINTER_MOVEFILE +{ "copyf", { 8, 8 } }, //POINTER_COPYFILE +{ "linkf", { 8, 8 } }, //POINTER_LINKFILE +{ "moveflnk", { 8, 8 } }, //POINTER_MOVEFILELINK +{ "copyflnk", { 8, 8 } }, //POINTER_COPYFILELINK +{ "movef2", { 7, 8 } }, //POINTER_MOVEFILES +{ "copyf2", { 7, 8 } }, //POINTER_COPYFILES +{ "notallow", { 15, 15 } }, //POINTER_NOTALLOWED +{ "dline", { 8, 8 } }, //POINTER_DRAW_LINE +{ "drect", { 8, 8 } }, //POINTER_DRAW_RECT +{ "dpolygon", { 8, 8 } }, //POINTER_DRAW_POLYGON +{ "dbezier", { 8, 8 } }, //POINTER_DRAW_BEZIER +{ "darc", { 8, 8 } }, //POINTER_DRAW_ARC +{ "dpie", { 8, 8 } }, //POINTER_DRAW_PIE +{ "dcirccut", { 8, 8 } }, //POINTER_DRAW_CIRCLECUT +{ "dellipse", { 8, 8 } }, //POINTER_DRAW_ELLIPSE +{ "dfree", { 8, 8 } }, //POINTER_DRAW_FREEHAND +{ "dconnect", { 8, 8 } }, //POINTER_DRAW_CONNECT +{ "dtext", { 8, 8 } }, //POINTER_DRAW_TEXT +{ "dcapt", { 8, 8 } }, //POINTER_DRAW_CAPTION +{ "chart", { 15, 16 } }, //POINTER_CHART +{ "detectiv", { 12, 13 } }, //POINTER_DETECTIVE +{ "pivotcol", { 7, 5 } }, //POINTER_PIVOT_COL +{ "pivotrow", { 8, 7 } }, //POINTER_PIVOT_ROW +{ "pivotfld", { 8, 7 } }, //POINTER_PIVOT_FIELD +{ "chain", { 0, 2 } }, //POINTER_CHAIN +{ "chainnot", { 2, 2 } }, //POINTER_CHAIN_NOTALLOWED +{ "timemove", { 16, 16 } }, //POINTER_TIMEEVENT_MOVE +{ "timesize", { 16, 17 } }, //POINTER_TIMEEVENT_SIZE +{ "asn", { 16, 12 } }, //POINTER_AUTOSCROLL_N +{ "ass", { 15, 19 } }, //POINTER_AUTOSCROLL_S +{ "asw", { 12, 15 } }, //POINTER_AUTOSCROLL_W +{ "ase", { 19, 16 } }, //POINTER_AUTOSCROLL_E +{ "asnw", { 10, 10 } }, //POINTER_AUTOSCROLL_NW +{ "asne", { 21, 10 } }, //POINTER_AUTOSCROLL_NE +{ "assw", { 21, 21 } }, //POINTER_AUTOSCROLL_SW +{ "asse", { 21, 21 } }, //POINTER_AUTOSCROLL_SE +{ "asns", { 15, 15 } }, //POINTER_AUTOSCROLL_NS +{ "aswe", { 15, 15 } }, //POINTER_AUTOSCROLL_WE +{ "asnswe", { 15, 15 } }, //POINTER_AUTOSCROLL_NSWE +{ "airbrush", { 5, 22 } }, //POINTER_AIRBRUSH +{ "vtext", { 15, 15 } }, //POINTER_TEXT_VERTICAL +{ "pivotdel", { 18, 15 } }, //POINTER_PIVOT_DELETE +{ "tblsels", { 15, 30 } }, //POINTER_TAB_SELECT_S +{ "tblsele", { 30, 16 } }, //POINTER_TAB_SELECT_E +{ "tblselse", { 30, 30 } }, //POINTER_TAB_SELECT_SE +{ "tblselw", { 1, 16 } }, //POINTER_TAB_SELECT_W +{ "tblselsw", { 1, 30 } }, //POINTER_TAB_SELECT_SW +{ "pntbrsh", { 9, 16 } } //POINTER_PAINTBRUSH +}; + +NSCursor* SalData::getCursor( PointerStyle i_eStyle ) +{ + if( i_eStyle >= POINTER_COUNT ) + return nil; + + NSCursor* pCurs = maCursors[ i_eStyle ]; + if( pCurs == INVALID_CURSOR_PTR ) + { + pCurs = nil; + if( aCursorTab[ i_eStyle ].pBaseName ) + { + NSPoint aHotSpot = aCursorTab[ i_eStyle ].aHotSpot; + CFStringRef pCursorName = + CFStringCreateWithCStringNoCopy( + kCFAllocatorDefault, + aCursorTab[ i_eStyle ].pBaseName, + kCFStringEncodingASCII, + kCFAllocatorNull ); + CFBundleRef hMain = CFBundleGetMainBundle(); + CFURLRef hURL = CFBundleCopyResourceURL( hMain, pCursorName, CFSTR("png"), CFSTR("cursors") ); + if( hURL ) + { + pCurs = [[NSCursor alloc] initWithImage: [[NSImage alloc] initWithContentsOfURL: (NSURL*)hURL] hotSpot: aHotSpot]; + CFRelease( hURL ); + } + CFRelease( pCursorName ); + } + maCursors[ i_eStyle ] = pCurs; + } + return pCurs; +} + +NSStatusItem* SalData::getStatusItem() +{ + SalData* pData = GetSalData(); + if( ! pData->mpStatusItem ) + { + NSStatusBar* pStatBar =[NSStatusBar systemStatusBar]; + if( pStatBar ) + { + pData->mpStatusItem = [pStatBar statusItemWithLength: NSVariableStatusItemLength]; + [pData->mpStatusItem retain]; + OOStatusItemView* pView = [[OOStatusItemView alloc] init]; + [pData->mpStatusItem setView: pView ]; + [pView display]; + } + } + return pData->mpStatusItem; +} diff --git a/vcl/aqua/source/app/salinst.cxx b/vcl/aqua/source/app/salinst.cxx new file mode 100644 index 000000000000..cce018ac6229 --- /dev/null +++ b/vcl/aqua/source/app/salinst.cxx @@ -0,0 +1,1323 @@ +/************************************************************************* + * + * 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 "osl/process.h" +#include "rtl/ustrbuf.hxx" +#include "vcl/svapp.hxx" +#include "vcl/print.h" +#include "vcl/salimestatus.hxx" +#include "vcl/window.hxx" +#include "vcl/timer.hxx" +#include "vcl/impbmp.hxx" + +#include "saldata.hxx" +#include "salinst.h" +#include "salframe.h" +#include "salobj.h" +#include "salsys.h" +#include "salvd.h" +#include "salbmp.h" +#include "salprn.h" +#include "saltimer.h" +#include "vclnsapp.h" + +#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" +#include <tools/solarmutex.hxx> + +using namespace std; +using namespace ::com::sun::star; + +extern BOOL ImplSVMain(); + +static 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]; +} + +BOOL ImplSVMainHook( 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 + +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.mbPrinterPullModel = true; + 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, USHORT 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; +} + +// ----------------------------------------------------------------------- + +ULONG AquaSalInstance::ReleaseYieldMutex() +{ + SalYieldMutex* pYieldMutex = mpSalYieldMutex; + if ( pYieldMutex->GetThreadId() == + vos::OThread::getCurrentIdentifier() ) + { + ULONG nCount = pYieldMutex->GetAcquireCount(); + ULONG n = nCount; + while ( n ) + { + pYieldMutex->release(); + n--; + } + + return nCount; + } + else + return 0; +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::AcquireYieldMutex( ULONG nCount ) +{ + SalYieldMutex* pYieldMutex = mpSalYieldMutex; + while ( nCount ) + { + pYieldMutex->acquire(); + nCount--; + } +} + +// ----------------------------------------------------------------------- + +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: + { + 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 ) + { + 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 + { + 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 ) + { + 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 }; + 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( USHORT 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* pSystemParentData, ULONG nSalFrameStyle ) +{ + return NULL; +} + +// ----------------------------------------------------------------------- + +SalFrame* AquaSalInstance::CreateFrame( SalFrame* pParent, 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 */, 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* pInfo ) +{ +} + +// ----------------------------------------------------------------------- + +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* pInstance, bool(*pCallback)(void*,void*,int) ) +{ +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::SetErrorEventCallback( void* pInstance, bool(*pCallback)(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; +} diff --git a/vcl/aqua/source/app/salnstimer.mm b/vcl/aqua/source/app/salnstimer.mm new file mode 100755 index 000000000000..73e49fd99c61 --- /dev/null +++ b/vcl/aqua/source/app/salnstimer.mm @@ -0,0 +1,56 @@ +/************************************************************************* + * + * 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 "saltimer.h" +#include "salnstimer.h" +#include "salinst.h" +#include "saldata.hxx" + +#include "vcl/svdata.hxx" + +@implementation TimerCallbackCaller +-(void)timerElapsed:(NSTimer*)pTimer +{ + ImplSVData* pSVData = ImplGetSVData(); + if( AquaSalTimer::bDispatchTimer ) + { + if( pSVData->mpSalTimer ) + { + YIELD_GUARD; + pSVData->mpSalTimer->CallCallback(); + + // NSTimer does not end nextEventMatchingMask of NSApplication + // so we need to wakeup a waiting Yield to inform it something happened + GetSalData()->mpFirstInstance->wakeupYield(); + } + } +} +@end + diff --git a/vcl/aqua/source/app/salsys.cxx b/vcl/aqua/source/app/salsys.cxx new file mode 100644 index 000000000000..cf5cf00b7fe4 --- /dev/null +++ b/vcl/aqua/source/app/salsys.cxx @@ -0,0 +1,220 @@ +/************************************************************************* + * + * 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 "tools/rc.hxx" +#include "vcl/svids.hrc" +#include "vcl/button.hxx" + +#include "salsys.h" +#include "saldata.hxx" +#include "salinst.h" +#include "rtl/ustrbuf.hxx" + +using namespace rtl; + +// ======================================================================= + +AquaSalSystem::~AquaSalSystem() +{ +} + +unsigned int AquaSalSystem::GetDisplayScreenCount() +{ + NSArray* pScreens = [NSScreen screens]; + return pScreens ? [pScreens count] : 1; +} + +bool AquaSalSystem::IsMultiDisplay() +{ + return false; +} + +unsigned int AquaSalSystem::GetDefaultDisplayNumber() +{ + return 0; +} + +Rectangle AquaSalSystem::GetDisplayScreenPosSizePixel( unsigned int nScreen ) +{ + NSArray* pScreens = [NSScreen screens]; + Rectangle aRet; + NSScreen* pScreen = nil; + if( pScreens && nScreen < [pScreens count] ) + pScreen = [pScreens objectAtIndex: nScreen]; + else + pScreen = [NSScreen mainScreen]; + + if( pScreen ) + { + NSRect aFrame = [pScreen frame]; + aRet = Rectangle( Point( static_cast<long int>(aFrame.origin.x), static_cast<long int>(aFrame.origin.y) ), + Size( static_cast<long int>(aFrame.size.width), static_cast<long int>(aFrame.size.height) ) ); + } + return aRet; +} + +Rectangle AquaSalSystem::GetDisplayWorkAreaPosSizePixel( unsigned int nScreen ) +{ + NSArray* pScreens = [NSScreen screens]; + Rectangle aRet; + NSScreen* pScreen = nil; + if( pScreens && nScreen < [pScreens count] ) + pScreen = [pScreens objectAtIndex: nScreen]; + else + pScreen = [NSScreen mainScreen]; + + if( pScreen ) + { + NSRect aFrame = [pScreen visibleFrame]; + aRet = Rectangle( Point( static_cast<long int>(aFrame.origin.x), static_cast<long int>(aFrame.origin.y) ), + Size( static_cast<long int>(aFrame.size.width), static_cast<long int>(aFrame.size.height) ) ); + } + return aRet; +} + +rtl::OUString AquaSalSystem::GetScreenName( unsigned int nScreen ) +{ + NSArray* pScreens = [NSScreen screens]; + OUString aRet; + if( nScreen < [pScreens count] ) + { + ResMgr* pMgr = ImplGetResMgr(); + if( pMgr ) + { + String aScreenName( ResId( SV_MAC_SCREENNNAME, *pMgr ) ); + aScreenName.SearchAndReplaceAllAscii( "%d", String::CreateFromInt32( nScreen ) ); + aRet = aScreenName; + } + } + return aRet; +} + +static NSString* getStandardString( int nButtonId ) +{ + rtl::OUString aText( Button::GetStandardText( nButtonId ) ); + if( ! aText.getLength() ) // this is for bad cases, we might be missing the vcl resource + { + switch( nButtonId ) + { + case BUTTON_OK: aText = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "OK" ) );break; + case BUTTON_ABORT: aText = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Abort" ) );break; + case BUTTON_CANCEL: aText = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Cancel" ) );break; + case BUTTON_RETRY: aText = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Retry" ) );break; + case BUTTON_YES: aText = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Yes" ) );break; + case BUTTON_NO : aText = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "No" ) );break; + } + } + return aText.getLength() ? CreateNSString( aText) : nil; +} + +int AquaSalSystem::ShowNativeMessageBox( const String& rTitle, + const String& rMessage, + int nButtonCombination, + int nDefaultButton) +{ + NSString* pTitle = CreateNSString( rTitle ); + NSString* pMessage = CreateNSString( rMessage ); + + struct id_entry + { + int nCombination; + int nDefaultButton; + int nTextIds[3]; + } aButtonIds[] = + { + { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_OK, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_OK, { BUTTON_OK, -1, -1 } }, + { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_OK_CANCEL, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_OK, { BUTTON_OK, BUTTON_CANCEL, -1 } }, + { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_OK_CANCEL, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_CANCEL, { BUTTON_CANCEL, BUTTON_OK, -1 } }, + { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_ABORT_RETRY_IGNORE, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_ABORT, { BUTTON_ABORT, BUTTON_IGNORE, BUTTON_RETRY } }, + { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_ABORT_RETRY_IGNORE, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_RETRY, { BUTTON_RETRY, BUTTON_IGNORE, BUTTON_ABORT } }, + { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_ABORT_RETRY_IGNORE, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_IGNORE, { BUTTON_IGNORE, BUTTON_IGNORE, BUTTON_ABORT } }, + { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_YES_NO_CANCEL, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_YES, { BUTTON_YES, BUTTON_NO, BUTTON_CANCEL } }, + { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_YES_NO_CANCEL, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_NO, { BUTTON_NO, BUTTON_YES, BUTTON_CANCEL } }, + { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_YES_NO_CANCEL, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_CANCEL, { BUTTON_CANCEL, BUTTON_YES, BUTTON_NO } }, + { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_YES_NO, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_YES, { BUTTON_YES, BUTTON_NO, -1 } }, + { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_YES_NO, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_NO, { BUTTON_NO, BUTTON_YES, -1 } }, + { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_RETRY_CANCEL, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_RETRY, { BUTTON_RETRY, BUTTON_CANCEL, -1 } }, + { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_RETRY_CANCEL, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_CANCEL, { BUTTON_CANCEL, BUTTON_RETRY, -1 } } + }; + + NSString* pDefText = nil; + NSString* pAltText = nil; + NSString* pOthText = nil; + + unsigned int nC; + for( nC = 0; nC < sizeof(aButtonIds)/sizeof(aButtonIds[0]); nC++ ) + { + if( aButtonIds[nC].nCombination == nButtonCombination ) + { + if( aButtonIds[nC].nDefaultButton == nDefaultButton ) + { + if( aButtonIds[nC].nTextIds[0] != -1 ) + pDefText = getStandardString( aButtonIds[nC].nTextIds[0] ); + if( aButtonIds[nC].nTextIds[1] != -1 ) + pAltText = getStandardString( aButtonIds[nC].nTextIds[1] ); + if( aButtonIds[nC].nTextIds[2] != -1 ) + pOthText = getStandardString( aButtonIds[nC].nTextIds[2] ); + break; + } + } + } + + + int nResult = NSRunAlertPanel( pTitle, pMessage, pDefText, pAltText, pOthText ); + + if( pTitle ) + [pTitle release]; + if( pMessage ) + [pMessage release]; + if( pDefText ) + [pDefText release]; + if( pAltText ) + [pAltText release]; + if( pOthText ) + [pOthText release]; + + int nRet = 0; + if( nC < sizeof(aButtonIds)/sizeof(aButtonIds[0]) && nResult >= 1 && nResult <= 3 ) + { + int nPressed = aButtonIds[nC].nTextIds[nResult-1]; + switch( nPressed ) + { + case BUTTON_NO: nRet = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_NO; break; + case BUTTON_YES: nRet = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_YES; break; + case BUTTON_OK: nRet = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_OK; break; + case BUTTON_CANCEL: nRet = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_CANCEL; break; + case BUTTON_ABORT: nRet = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_ABORT; break; + case BUTTON_RETRY: nRet = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_RETRY; break; + case BUTTON_IGNORE: nRet = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_IGNORE; break; + } + } + + return nRet; +} diff --git a/vcl/aqua/source/app/saltimer.cxx b/vcl/aqua/source/app/saltimer.cxx new file mode 100644 index 000000000000..724857e92a0c --- /dev/null +++ b/vcl/aqua/source/app/saltimer.cxx @@ -0,0 +1,135 @@ +/************************************************************************* + * + * 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 "saltimer.h" +#include "salnstimer.h" +#include "saldata.hxx" +#include "salframe.h" +#include "salinst.h" + +// ======================================================================= + +NSTimer* AquaSalTimer::pRunningTimer = nil; +bool AquaSalTimer::bDispatchTimer = false; + + +void ImplSalStartTimer( ULONG nMS ) +{ + SalData* pSalData = GetSalData(); + if( pSalData->mpFirstInstance->isNSAppThread() ) + { + AquaSalTimer::bDispatchTimer = true; + NSTimeInterval aTI = double(nMS)/1000.0; + if( AquaSalTimer::pRunningTimer != nil ) + { + if( [AquaSalTimer::pRunningTimer timeInterval] == aTI ) + // set new fire date + [AquaSalTimer::pRunningTimer setFireDate: [NSDate dateWithTimeIntervalSinceNow: aTI]]; + else + { + [AquaSalTimer::pRunningTimer invalidate]; + AquaSalTimer::pRunningTimer = nil; + } + } + if( AquaSalTimer::pRunningTimer == nil ) + { + AquaSalTimer::pRunningTimer = [NSTimer scheduledTimerWithTimeInterval: aTI + target: [[[TimerCallbackCaller alloc] init] autorelease] + selector: @selector(timerElapsed:) + userInfo: nil + repeats: YES]; + /* #i84055# add timer to tracking run loop mode, + so they also elapse while e.g. life resize + */ + [[NSRunLoop currentRunLoop] addTimer: AquaSalTimer::pRunningTimer forMode: NSEventTrackingRunLoopMode]; + } + } + else + { + SalData::ensureThreadAutoreleasePool(); + // post an event so we can get into the main thread + NSPoint aPt = { 0, 0 }; + NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined + location: aPt + modifierFlags: 0 + timestamp: [NSDate timeIntervalSinceReferenceDate] + windowNumber: 0 + context: nil + subtype: AquaSalInstance::AppStartTimerEvent + data1: (int)nMS + data2: 0 ]; + if( pEvent ) + [NSApp postEvent: pEvent atStart: YES]; + } +} + +void ImplSalStopTimer() +{ + AquaSalTimer::bDispatchTimer = false; +} + +void AquaSalTimer::handleStartTimerEvent( NSEvent* pEvent ) +{ + ImplSVData* pSVData = ImplGetSVData(); + if( pSVData->mpSalTimer ) + { + NSTimeInterval posted = [pEvent timestamp] + NSTimeInterval([pEvent data1])/1000.0; + NSTimeInterval current = [NSDate timeIntervalSinceReferenceDate]; + if( (posted - current) <= 0.0 ) + { + YIELD_GUARD; + // timer already elapsed since event posted + pSVData->mpSalTimer->CallCallback(); + } + ImplSalStartTimer( ULONG( [pEvent data1] ) ); + } + +} + +AquaSalTimer::AquaSalTimer( ) +{ +} + +AquaSalTimer::~AquaSalTimer() +{ + ImplSalStopTimer(); +} + +void AquaSalTimer::Start( ULONG nMS ) +{ + ImplSalStartTimer( nMS ); +} + +void AquaSalTimer::Stop() +{ + ImplSalStopTimer(); +} + + diff --git a/vcl/aqua/source/app/vclnsapp.mm b/vcl/aqua/source/app/vclnsapp.mm new file mode 100755 index 000000000000..f33599fa086e --- /dev/null +++ b/vcl/aqua/source/app/vclnsapp.mm @@ -0,0 +1,518 @@ +/************************************************************************* + * + * 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 "vclnsapp.h" +#include "salinst.h" +#include "saldata.hxx" +#include "salframe.h" +#include "salframeview.h" + +#include "vcl/window.hxx" +#include "vcl/svapp.hxx" +#include "vcl/cmdevt.hxx" +#include "rtl/ustrbuf.hxx" + +#include "premac.h" +#import "Carbon/Carbon.h" +#import "apple_remote/RemoteControl.h" +#include "postmac.h" + + +@implementation CocoaThreadEnabler +-(void)enableCocoaThreads:(id)param +{ + // do nothing, this is just to start an NSThread and therefore put + // Cocoa into multithread mode +} +@end + +@implementation VCL_NSApplication +-(void)sendEvent:(NSEvent*)pEvent +{ + NSEventType eType = [pEvent type]; + if( eType == NSApplicationDefined ) + GetSalData()->mpFirstInstance->handleAppDefinedEvent( pEvent ); + else if( eType == NSKeyDown && ([pEvent modifierFlags] & NSCommandKeyMask) != 0 ) + { + NSWindow* pKeyWin = [NSApp keyWindow]; + if( pKeyWin && [pKeyWin isKindOfClass: [SalFrameWindow class]] ) + { + AquaSalFrame* pFrame = [(SalFrameWindow*)pKeyWin getSalFrame]; + // handle Cmd-W + // FIXME: the correct solution would be to handle this in framework + // in the menu code + // however that is currently being revised, so let's use a preliminary solution here + // this hack is based on assumption + // a) Cmd-W is the same in all languages in OOo's menu conig + // b) Cmd-W is the same in all languages in on MacOS + // for now this seems to be true + unsigned int nModMask = ([pEvent modifierFlags] & (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask|NSCommandKeyMask)); + if( (pFrame->mnStyleMask & NSClosableWindowMask) != 0 ) + { + if( nModMask == NSCommandKeyMask + && [[pEvent charactersIgnoringModifiers] isEqualToString: @"w"] ) + { + [pFrame->getWindow() windowShouldClose: nil]; + return; + } + } + + /* + * #i98949# - Cmd-M miniaturize window, Cmd-Option-M miniaturize all windows + */ + if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"m"] ) + { + if ( nModMask == NSCommandKeyMask && ([pFrame->getWindow() styleMask] & NSMiniaturizableWindowMask) ) + { + [pFrame->getWindow() performMiniaturize: nil]; + return; + } + + if ( nModMask == ( NSCommandKeyMask | NSAlternateKeyMask ) ) + { + [NSApp miniaturizeAll: nil]; + return; + } + } + + // #i90083# handle frame switching + // FIXME: lousy workaround + if( (nModMask & (NSControlKeyMask|NSAlternateKeyMask)) == 0 ) + { + if( [[pEvent characters] isEqualToString: @"<"] || + [[pEvent characters] isEqualToString: @"~"] ) + { + [self cycleFrameForward: pFrame]; + return; + } + else if( [[pEvent characters] isEqualToString: @">"] || + [[pEvent characters] isEqualToString: @"`"] ) + { + [self cycleFrameBackward: pFrame]; + return; + } + } + + // get information whether the event was handled; keyDown returns nothing + GetSalData()->maKeyEventAnswer[ pEvent ] = false; + bool bHandled = false; + + // dispatch to view directly to avoid the key event being consumed by the menubar + // popup windows do not get the focus, so they don't get these either + // simplest would be dispatch this to the key window always if it is without parent + // however e.g. in document we want the menu shortcut if e.g. the stylist has focus + if( pFrame->mpParent && (pFrame->mnStyle & SAL_FRAME_STYLE_FLOAT) == 0 ) + { + [[pKeyWin contentView] keyDown: pEvent]; + bHandled = GetSalData()->maKeyEventAnswer[ pEvent ]; + } + + // see whether the main menu consumes this event + // if not, we want to dispatch it ourselves. Unless we do this "trick" + // the main menu just beeps for an unknown or disabled key equivalent + // and swallows the event wholesale + NSMenu* pMainMenu = [NSApp mainMenu]; + if( ! bHandled && (pMainMenu == 0 || ! [pMainMenu performKeyEquivalent: pEvent]) ) + { + [[pKeyWin contentView] keyDown: pEvent]; + bHandled = GetSalData()->maKeyEventAnswer[ pEvent ]; + } + else + bHandled = true; // event handled already or main menu just handled it + + GetSalData()->maKeyEventAnswer.erase( pEvent ); + if( bHandled ) + return; + } + else if( pKeyWin ) + { + // #i94601# a window not of vcl's making has the focus. + // Since our menus do not invoke the usual commands + // try to play nice with native windows like the file dialog + // and emulate them + // precondition: this ONLY works because CMD-V (paste), CMD-C (copy) and CMD-X (cut) are + // NOT localized, that is the same in all locales. Should this be + // different in any locale, this hack will fail. + unsigned int nModMask = ([pEvent modifierFlags] & (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask|NSCommandKeyMask)); + if( nModMask == NSCommandKeyMask ) + { + + if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"v"] ) + { + if( [NSApp sendAction: @selector(paste:) to: nil from: nil] ) + return; + } + else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"c"] ) + { + if( [NSApp sendAction: @selector(copy:) to: nil from: nil] ) + return; + } + else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"x"] ) + { + if( [NSApp sendAction: @selector(cut:) to: nil from: nil] ) + return; + } + } + } + } + else if( eType == NSScrollWheel && ( GetSalData()->mnSystemVersion < VER_LEOPARD /* fixed in Leopard and above */ ) ) + { + + NSWindow* pWin = [pEvent window]; + // on Tiger wheel events do not reach non key windows + // which probably should be considered a bug + if( [pWin isKindOfClass: [SalFrameWindow class]] && [pWin canBecomeKeyWindow] == NO ) + { + [[pWin contentView] scrollWheel: pEvent]; + return; + } + } + [super sendEvent: pEvent]; +} + +-(void)sendSuperEvent:(NSEvent*)pEvent +{ + [super sendEvent: pEvent]; +} + +-(void)cycleFrameForward: (AquaSalFrame*)pCurFrame +{ + // find current frame in list + std::list< AquaSalFrame* >& rFrames( GetSalData()->maFrames ); + std::list< AquaSalFrame* >::iterator it = rFrames.begin(); + for( ; it != rFrames.end() && *it != pCurFrame; ++it ) + ; + if( it != rFrames.end() ) + { + // now find the next frame (or end) + do + { + ++it; + if( it != rFrames.end() ) + { + if( (*it)->mpDockMenuEntry != NULL && + (*it)->mbShown ) + { + [(*it)->getWindow() makeKeyAndOrderFront: NSApp]; + return; + } + } + } while( it != rFrames.end() ); + // cycle around, find the next up to pCurFrame + it = rFrames.begin(); + while( *it != pCurFrame ) + { + if( (*it)->mpDockMenuEntry != NULL && + (*it)->mbShown ) + { + [(*it)->getWindow() makeKeyAndOrderFront: NSApp]; + return; + } + ++it; + } + } +} + +-(void)cycleFrameBackward: (AquaSalFrame*)pCurFrame +{ + // do the same as cycleFrameForward only with a reverse iterator + + // find current frame in list + std::list< AquaSalFrame* >& rFrames( GetSalData()->maFrames ); + std::list< AquaSalFrame* >::reverse_iterator it = rFrames.rbegin(); + for( ; it != rFrames.rend() && *it != pCurFrame; ++it ) + ; + if( it != rFrames.rend() ) + { + // now find the next frame (or end) + do + { + ++it; + if( it != rFrames.rend() ) + { + if( (*it)->mpDockMenuEntry != NULL && + (*it)->mbShown ) + { + [(*it)->getWindow() makeKeyAndOrderFront: NSApp]; + return; + } + } + } while( it != rFrames.rend() ); + // cycle around, find the next up to pCurFrame + it = rFrames.rbegin(); + while( *it != pCurFrame ) + { + if( (*it)->mpDockMenuEntry != NULL && + (*it)->mbShown ) + { + [(*it)->getWindow() makeKeyAndOrderFront: NSApp]; + return; + } + ++it; + } + } +} + +-(NSMenu*)applicationDockMenu:(NSApplication *)sender +{ + return AquaSalInstance::GetDynamicDockMenu(); +} + +-(MacOSBOOL)application: (NSApplication*)app openFile: (NSString*)pFile +{ + const rtl::OUString aFile( GetOUString( pFile ) ); + if( ! AquaSalInstance::isOnCommandLine( aFile ) ) + { + const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(), + APPEVENT_OPEN_STRING, aFile ); + AquaSalInstance::aAppEventList.push_back( pAppEvent ); + } + return YES; +} + +-(void)application: (NSApplication*) app openFiles: (NSArray*)files +{ + rtl::OUStringBuffer aFileList( 256 ); + + NSEnumerator* it = [files objectEnumerator]; + NSString* pFile = nil; + + while( (pFile = [it nextObject]) != nil ) + { + const rtl::OUString aFile( GetOUString( pFile ) ); + if( ! AquaSalInstance::isOnCommandLine( aFile ) ) + { + if( aFileList.getLength() > 0 ) + aFileList.append( sal_Unicode( APPEVENT_PARAM_DELIMITER ) ); + aFileList.append( aFile ); + } + } + + if( aFileList.getLength() ) + { + // we have no back channel here, we have to assume success, in which case + // replyToOpenOrPrint does not need to be called according to documentation + // [app replyToOpenOrPrint: NSApplicationDelegateReplySuccess]; + const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(), + APPEVENT_OPEN_STRING, aFileList.makeStringAndClear() ); + AquaSalInstance::aAppEventList.push_back( pAppEvent ); + } +} + +-(MacOSBOOL)application: (NSApplication*)app printFile: (NSString*)pFile +{ + const rtl::OUString aFile( GetOUString( pFile ) ); + const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(), + APPEVENT_PRINT_STRING, aFile ); + AquaSalInstance::aAppEventList.push_back( pAppEvent ); + return YES; +} +-(NSApplicationPrintReply)application: (NSApplication *) app printFiles:(NSArray *)files withSettings: (NSDictionary *)printSettings showPrintPanels:(MacOSBOOL)bShowPrintPanels +{ + // currently ignores print settings an bShowPrintPanels + rtl::OUStringBuffer aFileList( 256 ); + + NSEnumerator* it = [files objectEnumerator]; + NSString* pFile = nil; + + while( (pFile = [it nextObject]) != nil ) + { + if( aFileList.getLength() > 0 ) + aFileList.append( sal_Unicode( APPEVENT_PARAM_DELIMITER ) ); + aFileList.append( GetOUString( pFile ) ); + } + const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(), + APPEVENT_PRINT_STRING, aFileList.makeStringAndClear() ); + AquaSalInstance::aAppEventList.push_back( pAppEvent ); + // we have no back channel here, we have to assume success + // correct handling would be NSPrintingReplyLater and then send [app replyToOpenOrPrint] + return NSPrintingSuccess; +} + +-(NSApplicationTerminateReply)applicationShouldTerminate: (NSApplication *) app +{ + SalData* pSalData = GetSalData(); + #if 1 // currently do some really bad hack + if( ! pSalData->maFrames.empty() ) + { + /* #i92766# something really weird is going on with the retain count of + our windows; sometimes we get a duplicate free before exit on one of our + NSWindows. The reason is unclear; to avoid this currently we retain them once more + + FIXME: this is a really bad hack, relying on the system to catch the leaked + resources. Find out what really goes on here and fix it ! + */ + std::vector< NSWindow* > aHackRetainedWindows; + for( std::list< AquaSalFrame* >::iterator it = pSalData->maFrames.begin(); + it != pSalData->maFrames.end(); ++it ) + { + #if OSL_DEBUG_LEVEL > 1 + Window* pWin = (*it)->GetWindow(); + String aTitle = pWin->GetText(); + Window* pClient = pWin->ImplGetClientWindow(); + fprintf( stderr, "retaining %p (old count %d) windowtype=%s clienttyp=%s title=%s\n", + (*it)->mpWindow, [(*it)->mpWindow retainCount], + typeid(*pWin).name(), pClient ? typeid(*pClient).name() : "<nil>", + rtl::OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ).getStr() + ); + #endif + [(*it)->mpWindow retain]; + aHackRetainedWindows.push_back( (*it)->mpWindow ); + } + if( pSalData->maFrames.front()->CallCallback( SALEVENT_SHUTDOWN, NULL ) ) + { + for( std::vector< NSWindow* >::iterator it = aHackRetainedWindows.begin(); + it != aHackRetainedWindows.end(); ++it ) + { + // clean up the retaing count again from the shutdown workaround + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "releasing %p\n", (*it) ); + #endif + [(*it) release]; + } + return NSTerminateCancel; + } + #if OSL_DEBUG_LEVEL > 1 + for( std::list< AquaSalFrame* >::iterator it = pSalData->maFrames.begin(); + it != pSalData->maFrames.end(); ++it ) + { + Window* pWin = (*it)->GetWindow(); + String aTitle = pWin->GetText(); + Window* pClient = pWin->ImplGetClientWindow(); + fprintf( stderr, "frame still alive: NSWindow %p windowtype=%s clienttyp=%s title=%s\n", + (*it)->mpWindow, typeid(*pWin).name(), pClient ? typeid(*pClient).name() : "<nil>", + rtl::OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ).getStr() + ); + } + #endif + } + #else // the clean version follows + return pSalData->maFrames.front()->CallCallback( SALEVENT_SHUTDOWN, NULL ) ? NSTerminateCancel : NSTerminateNow; + #endif + return NSTerminateNow; +} + +-(void)systemColorsChanged: (NSNotification*) pNotification +{ + const SalData* pSalData = GetSalData(); + if( !pSalData->maFrames.empty() ) + pSalData->maFrames.front()->CallCallback( SALEVENT_SETTINGSCHANGED, NULL ); +} + +-(void)screenParametersChanged: (NSNotification*) pNotification +{ + SalData* pSalData = GetSalData(); + std::list< AquaSalFrame* >::iterator it; + for( it = pSalData->maFrames.begin(); it != pSalData->maFrames.end(); ++it ) + { + (*it)->screenParametersChanged(); + } +} + +-(void)scrollbarVariantChanged: (NSNotification*) pNotification +{ + GetSalData()->mpFirstInstance->delayedSettingsChanged( true ); +} + +-(void)scrollbarSettingsChanged: (NSNotification*) pNotification +{ + GetSalData()->mpFirstInstance->delayedSettingsChanged( false ); +} + +-(void)addFallbackMenuItem: (NSMenuItem*)pNewItem +{ + AquaSalMenu::addFallbackMenuItem( pNewItem ); +} + +-(void)removeFallbackMenuItem: (NSMenuItem*)pItem +{ + AquaSalMenu::removeFallbackMenuItem( pItem ); +} + +-(void)addDockMenuItem: (NSMenuItem*)pNewItem +{ + NSMenu* pDock = AquaSalInstance::GetDynamicDockMenu(); + [pDock insertItem: pNewItem atIndex: [pDock numberOfItems]]; +} + +// for Apple Remote implementation + +#pragma mark - +#pragma mark NSApplication Delegates +- (void)applicationWillBecomeActive:(NSNotification *)aNotification { + if (GetSalData()->mpMainController->remoteControl) { + + // [remoteControl startListening: self]; + // does crash because the right thing to do is + // [GetSalData()->mpMainController->remoteControl startListening: self]; + // but the instance variable 'remoteControl' is declared protected + // workaround : declare remoteControl instance variable as public in RemoteMainController.m + + [GetSalData()->mpMainController->remoteControl startListening: self]; +#ifdef DEBUG + NSLog(@"Apple Remote will become active - Using remote controls"); +#endif + } +} + +- (void)applicationWillResignActive:(NSNotification *)aNotification { + if (GetSalData()->mpMainController->remoteControl) { + + // [remoteControl stopListening: self]; + // does crash because the right thing to do is + // [GetSalData()->mpMainController->remoteControl stopListening: self]; + // but the instance variable 'remoteControl' is declared protected + // workaround : declare remoteControl instance variable as public in RemoteMainController.m + + [GetSalData()->mpMainController->remoteControl stopListening: self]; +#ifdef DEBUG + NSLog(@"Apple Remote will resign active - Releasing remote controls"); +#endif + } +} + +- (MacOSBOOL)applicationShouldHandleReopen: (NSApplication*)pApp hasVisibleWindows: (MacOSBOOL) bWinVisible +{ + NSObject* pHdl = GetSalData()->mpDockIconClickHandler; + if( pHdl && [pHdl respondsToSelector: @selector(dockIconClicked:)] ) + { + [pHdl performSelector:@selector(dockIconClicked:) withObject: self]; + } + return YES; +} + +-(void)setDockIconClickHandler: (NSObject*)pHandler +{ + GetSalData()->mpDockIconClickHandler = pHandler; +} + + +@end + |