update dev300-m58
[ooovba.git] / vcl / aqua / source / app / vclnsapp.mm
blob43d44c709c12e5dd98a0948079ca55206d3e7ac3
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  * 
5  * Copyright 2008 by Sun Microsystems, Inc.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * $RCSfile: vclnsapp.mm,v $
10  * $Revision: 1.8.46.3 $
11  *
12  * This file is part of OpenOffice.org.
13  *
14  * OpenOffice.org is free software: you can redistribute it and/or modify
15  * it under the terms of the GNU Lesser General Public License version 3
16  * only, as published by the Free Software Foundation.
17  *
18  * OpenOffice.org is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU Lesser General Public License version 3 for more details
22  * (a copy is included in the LICENSE file that accompanied this code).
23  *
24  * You should have received a copy of the GNU Lesser General Public License
25  * version 3 along with OpenOffice.org.  If not, see
26  * <http://www.openoffice.org/license.html>
27  * for a copy of the LGPLv3 License.
28  *
29  ************************************************************************/
31  // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_vcl.hxx"
34 #include "vclnsapp.h"
35 #include "salinst.h"
36 #include "saldata.hxx"
37 #include "salframe.h"
38 #include "salframeview.h"
40 #include "vcl/window.hxx"
41 #include "vcl/svapp.hxx"
42 #include "vcl/cmdevt.hxx"
43 #include "rtl/ustrbuf.hxx"
45 #include "premac.h"
46 #import "Carbon/Carbon.h"
47 #import "apple_remote/RemoteControl.h"
48 #include "postmac.h"
51 @implementation CocoaThreadEnabler
52 -(void)enableCocoaThreads:(id)param
54     // do nothing, this is just to start an NSThread and therefore put
55     // Cocoa into multithread mode
57 @end
59 @implementation VCL_NSApplication
60 -(void)sendEvent:(NSEvent*)pEvent
62     NSEventType eType = [pEvent type];
63     if( eType == NSApplicationDefined )
64         GetSalData()->mpFirstInstance->handleAppDefinedEvent( pEvent );
65     else if( eType == NSKeyDown && ([pEvent modifierFlags] & NSCommandKeyMask) != 0 )
66     {
67         NSWindow* pKeyWin = [NSApp keyWindow];
68         if( pKeyWin && [pKeyWin isKindOfClass: [SalFrameWindow class]] )
69         {
70             AquaSalFrame* pFrame = [(SalFrameWindow*)pKeyWin getSalFrame];
71             // handle Cmd-W
72             // FIXME: the correct solution would be to handle this in framework
73             // in the menu code
74             // however that is currently being revised, so let's use a preliminary solution here
75             // this hack is based on assumption
76             // a) Cmd-W is the same in all languages in OOo's menu conig
77             // b) Cmd-W is the same in all languages in on MacOS
78             // for now this seems to be true
79             unsigned int nModMask = ([pEvent modifierFlags] & (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask|NSCommandKeyMask));
80             if( (pFrame->mnStyleMask & NSClosableWindowMask) != 0 )
81             {
82                 if( nModMask == NSCommandKeyMask
83                     && [[pEvent charactersIgnoringModifiers] isEqualToString: @"w"] )
84                 {
85                     [pFrame->getWindow() windowShouldClose: nil];
86                     return;
87                 }
88             }
89           
90             /*
91              * #i98949# - Cmd-M miniaturize window, Cmd-Option-M miniaturize all windows
92              */ 
93             if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"m"] )
94             {
95                 if ( nModMask == NSCommandKeyMask && ([pFrame->getWindow() styleMask] & NSMiniaturizableWindowMask) )
96                 {
97                     [pFrame->getWindow() performMiniaturize: nil];
98                     return;
99                 }
101                 if ( nModMask == ( NSCommandKeyMask | NSAlternateKeyMask ) )
102                 {
103                     [NSApp miniaturizeAll: nil];
104                     return;
105                 }
106             }
108             // get information whether the event was handled; keyDown returns nothing
109             GetSalData()->maKeyEventAnswer[ pEvent ] = false;
110             bool bHandled = false;
111             
112             // dispatch to view directly to avoid the key event being consumed by the menubar
113             // popup windows do not get the focus, so they don't get these either
114             // simplest would be dispatch this to the key window always if it is without parent
115             // however e.g. in document we want the menu shortcut if e.g. the stylist has focus
116             if( pFrame->mpParent && (pFrame->mnStyle & SAL_FRAME_STYLE_FLOAT) == 0 ) 
117             {
118                 [[pKeyWin contentView] keyDown: pEvent];
119                 bHandled = GetSalData()->maKeyEventAnswer[ pEvent ];
120             }
121             
122             // see whether the main menu consumes this event
123             // if not, we want to dispatch it ourselves. Unless we do this "trick"
124             // the main menu just beeps for an unknown or disabled key equivalent
125             // and swallows the event wholesale
126             NSMenu* pMainMenu = [NSApp mainMenu];
127             if( ! bHandled && (pMainMenu == 0 || ! [pMainMenu performKeyEquivalent: pEvent]) )
128             {
129                 [[pKeyWin contentView] keyDown: pEvent];
130                 bHandled = GetSalData()->maKeyEventAnswer[ pEvent ];
131             }
132             else
133                 bHandled = true;  // event handled already or main menu just handled it
135             GetSalData()->maKeyEventAnswer.erase( pEvent );
136             if( bHandled )
137                 return;
138         }
139         else if( pKeyWin )
140         {
141             // #i94601# a window not of vcl's making has the focus.
142             // Since our menus do not invoke the usual commands
143             // try to play nice with native windows like the file dialog
144             // and emulate them
145             // precondition: this ONLY works because CMD-V (paste), CMD-C (copy) and CMD-X (cut) are
146             // NOT localized, that is the same in all locales. Should this be
147             // different in any locale, this hack will fail.
148             unsigned int nModMask = ([pEvent modifierFlags] & (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask|NSCommandKeyMask));
149             if( nModMask == NSCommandKeyMask )
150             {
151                 
152                 if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"v"] )
153                 {
154                     if( [NSApp sendAction: @selector(paste:) to: nil from: nil] )
155                         return;
156                 }
157                 else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"c"] )
158                 {
159                     if( [NSApp sendAction: @selector(copy:) to: nil from: nil] )
160                         return;
161                 }
162                 else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"x"] )
163                 {
164                     if( [NSApp sendAction: @selector(cut:) to: nil from: nil] )
165                         return;
166                 }
167             }
168         }
169     }
170     else if( eType == NSScrollWheel && ( GetSalData()->mnSystemVersion < VER_LEOPARD /* fixed in Leopard and above */ ) )
171     {
173         NSWindow* pWin = [pEvent window];
174         // on Tiger wheel events do not reach non key windows
175         // which probably should be considered a bug
176         if( [pWin isKindOfClass: [SalFrameWindow class]] && [pWin canBecomeKeyWindow] == NO )
177         {
178             [[pWin contentView] scrollWheel: pEvent];
179             return;
180         }
181     }
182     [super sendEvent: pEvent];
185 -(void)sendSuperEvent:(NSEvent*)pEvent
187     [super sendEvent: pEvent];
190 -(NSMenu*)applicationDockMenu:(NSApplication *)sender
192     return AquaSalInstance::GetDynamicDockMenu();
195 -(MacOSBOOL)application: (NSApplication*)app openFile: (NSString*)pFile
197     const rtl::OUString aFile( GetOUString( pFile ) );
198     if( ! AquaSalInstance::isOnCommandLine( aFile ) )
199     {
200         const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(),
201                                                     APPEVENT_OPEN_STRING, aFile );
202         AquaSalInstance::aAppEventList.push_back( pAppEvent );
203     }
204     return YES;
207 -(void)application: (NSApplication*) app openFiles: (NSArray*)files
209     rtl::OUStringBuffer aFileList( 256 );
210     
211     NSEnumerator* it = [files objectEnumerator];
212     NSString* pFile = nil;
213     
214     while( (pFile = [it nextObject]) != nil )
215     {
216         const rtl::OUString aFile( GetOUString( pFile ) );
217         if( ! AquaSalInstance::isOnCommandLine( aFile ) )
218         {
219             if( aFileList.getLength() > 0 )
220                 aFileList.append( sal_Unicode( APPEVENT_PARAM_DELIMITER ) );
221             aFileList.append( aFile );
222         }
223     }
224     
225     if( aFileList.getLength() )
226     {
227         // we have no back channel here, we have to assume success, in which case
228         // replyToOpenOrPrint does not need to be called according to documentation
229         // [app replyToOpenOrPrint: NSApplicationDelegateReplySuccess];
230         const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(),
231                                                     APPEVENT_OPEN_STRING, aFileList.makeStringAndClear() );
232         AquaSalInstance::aAppEventList.push_back( pAppEvent );
233     }
236 -(MacOSBOOL)application: (NSApplication*)app printFile: (NSString*)pFile
238     const rtl::OUString aFile( GetOUString( pFile ) );
239         const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(),
240                                                 APPEVENT_PRINT_STRING, aFile );
241         AquaSalInstance::aAppEventList.push_back( pAppEvent );
242     return YES;
244 -(NSApplicationPrintReply)application: (NSApplication *) app printFiles:(NSArray *)files withSettings: (NSDictionary *)printSettings showPrintPanels:(MacOSBOOL)bShowPrintPanels
246     // currently ignores print settings an bShowPrintPanels
247     rtl::OUStringBuffer aFileList( 256 );
248     
249     NSEnumerator* it = [files objectEnumerator];
250     NSString* pFile = nil;
251     
252     while( (pFile = [it nextObject]) != nil )
253     {
254         if( aFileList.getLength() > 0 )
255             aFileList.append( sal_Unicode( APPEVENT_PARAM_DELIMITER ) );
256         aFileList.append( GetOUString( pFile ) );
257     }
258         const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(),
259                                                 APPEVENT_PRINT_STRING, aFileList.makeStringAndClear() );
260         AquaSalInstance::aAppEventList.push_back( pAppEvent );
261     // we have no back channel here, we have to assume success
262     // correct handling would be NSPrintingReplyLater and then send [app replyToOpenOrPrint]
263     return NSPrintingSuccess;
266 -(NSApplicationTerminateReply)applicationShouldTerminate: (NSApplication *) app
268     SalData* pSalData = GetSalData();
269     #if 1 // currently do some really bad hack
270     if( ! pSalData->maFrames.empty() )
271     {
272         /* #i92766# something really weird is going on with the retain count of
273            our windows; sometimes we get a duplicate free before exit on one of our
274            NSWindows. The reason is unclear; to avoid this currently we retain them once more
275            
276            FIXME: this is a really bad hack, relying on the system to catch the leaked
277            resources. Find out what really goes on here and fix it !
278         */
279         std::vector< NSWindow* > aHackRetainedWindows;
280         for( std::list< AquaSalFrame* >::iterator it = pSalData->maFrames.begin();
281              it != pSalData->maFrames.end(); ++it )
282         {
283             #if OSL_DEBUG_LEVEL > 1
284             Window* pWin = (*it)->GetWindow();
285             String aTitle = pWin->GetText();
286             Window* pClient = pWin->ImplGetClientWindow();
287             fprintf( stderr, "retaining %p (old count %d) windowtype=%s clienttyp=%s title=%s\n",
288                 (*it)->mpWindow, [(*it)->mpWindow retainCount],
289                 typeid(*pWin).name(), pClient ? typeid(*pClient).name() : "<nil>",
290                 rtl::OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ).getStr()
291                 );
292             #endif
293             [(*it)->mpWindow retain];
294             aHackRetainedWindows.push_back( (*it)->mpWindow ); 
295         }
296         if( pSalData->maFrames.front()->CallCallback( SALEVENT_SHUTDOWN, NULL ) )
297         {
298             for( std::vector< NSWindow* >::iterator it = aHackRetainedWindows.begin();
299                  it != aHackRetainedWindows.end(); ++it )
300             {
301                 // clean up the retaing count again from the shutdown workaround
302                 #if OSL_DEBUG_LEVEL > 1
303                 fprintf( stderr, "releasing %p\n", (*it) );
304                 #endif
305                 [(*it) release];
306             }
307             return NSTerminateCancel;
308         }
309         #if OSL_DEBUG_LEVEL > 1
310         for( std::list< AquaSalFrame* >::iterator it = pSalData->maFrames.begin();
311              it != pSalData->maFrames.end(); ++it )
312         {
313             Window* pWin = (*it)->GetWindow();
314             String aTitle = pWin->GetText();
315             Window* pClient = pWin->ImplGetClientWindow();
316             fprintf( stderr, "frame still alive: NSWindow %p windowtype=%s clienttyp=%s title=%s\n",
317                 (*it)->mpWindow, typeid(*pWin).name(), pClient ? typeid(*pClient).name() : "<nil>",
318                 rtl::OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ).getStr()
319                 );
320         }
321         #endif
322     }
323     #else // the clean version follows
324     return pSalData->maFrames.front()->CallCallback( SALEVENT_SHUTDOWN, NULL ) ? NSTerminateCancel : NSTerminateNow;
325     #endif
326     return NSTerminateNow;
329 -(void)systemColorsChanged: (NSNotification*) pNotification
331     const SalData* pSalData = GetSalData();
332         if( !pSalData->maFrames.empty() )
333                 pSalData->maFrames.front()->CallCallback( SALEVENT_SETTINGSCHANGED, NULL );
336 -(void)screenParametersChanged: (NSNotification*) pNotification
338     SalData* pSalData = GetSalData();
339     std::list< AquaSalFrame* >::iterator it;
340     for( it = pSalData->maFrames.begin(); it != pSalData->maFrames.end(); ++it )
341     {
342         (*it)->screenParametersChanged();
343     }
346 -(void)scrollbarVariantChanged: (NSNotification*) pNotification
348     GetSalData()->mpFirstInstance->delayedSettingsChanged( true );
351 -(void)scrollbarSettingsChanged: (NSNotification*) pNotification
353     GetSalData()->mpFirstInstance->delayedSettingsChanged( false );
356 -(void)addFallbackMenuItem: (NSMenuItem*)pNewItem
358     AquaSalMenu::addFallbackMenuItem( pNewItem );
361 -(void)removeFallbackMenuItem: (NSMenuItem*)pItem
363     AquaSalMenu::removeFallbackMenuItem( pItem );
366 -(void)addDockMenuItem: (NSMenuItem*)pNewItem
368     NSMenu* pDock = AquaSalInstance::GetDynamicDockMenu();
369     [pDock insertItem: pNewItem atIndex: [pDock numberOfItems]];
372 // for Apple Remote implementation
374 #pragma mark -
375 #pragma mark NSApplication Delegates
376 - (void)applicationWillBecomeActive:(NSNotification *)aNotification {
377     if (GetSalData()->mpMainController->remoteControl) {
379         // [remoteControl startListening: self];
380         // does crash because the right thing to do is 
381         // [GetSalData()->mpMainController->remoteControl startListening: self];
382         // but the instance variable 'remoteControl' is declared protected
383         // workaround : declare remoteControl instance variable as public in RemoteMainController.m
385         [GetSalData()->mpMainController->remoteControl startListening: self];
386 #ifdef DEBUG
387         NSLog(@"Apple Remote will become active - Using remote controls");
388 #endif
389     }
392 - (void)applicationWillResignActive:(NSNotification *)aNotification {
393     if (GetSalData()->mpMainController->remoteControl) {
395         // [remoteControl stopListening: self];
396         // does crash because the right thing to do is 
397         // [GetSalData()->mpMainController->remoteControl stopListening: self];
398         // but the instance variable 'remoteControl' is declared protected
399         // workaround : declare remoteControl instance variable as public in RemoteMainController.m
401         [GetSalData()->mpMainController->remoteControl stopListening: self]; 
402 #ifdef DEBUG
403         NSLog(@"Apple Remote will resign active - Releasing remote controls");
404 #endif
405     }
408 - (MacOSBOOL)applicationShouldHandleReopen: (NSApplication*)pApp hasVisibleWindows: (MacOSBOOL) bWinVisible
410     NSObject* pHdl = GetSalData()->mpDockIconClickHandler;
411     if( pHdl && [pHdl respondsToSelector: @selector(dockIconClicked:)] )
412     {
413         [pHdl performSelector:@selector(dockIconClicked:) withObject: self];
414     }
415     return YES;
418 -(void)setDockIconClickHandler: (NSObject*)pHandler
420     GetSalData()->mpDockIconClickHandler = pHandler;
424 @end