1 /////////////////////////////////////////////////////////////////////////////
 
   2 // Name:        src/osx/cocoa/utils.mm
 
   3 // Purpose:     various cocoa utility functions
 
   4 // Author:      Stefan Csomor
 
   7 // Copyright:   (c) Stefan Csomor
 
   8 // Licence:     wxWindows licence
 
   9 /////////////////////////////////////////////////////////////////////////////
 
  11 #include "wx/wxprec.h"
 
  19         #include "wx/dialog.h"
 
  20         #include "wx/toplevel.h"
 
  25 #include "wx/apptrait.h"
 
  27 #include "wx/osx/private.h"
 
  30 #if wxOSX_USE_COCOA_OR_CARBON
 
  31     #include <CoreServices/CoreServices.h>
 
  32     #include "wx/osx/dcclient.h"
 
  33     #include "wx/osx/private/timer.h"
 
  47 @implementation wxNSAppController
 
  49 - (void)applicationWillFinishLaunching:(NSNotification *)application
 
  51     wxUnusedVar(application);
 
  53     // we must install our handlers later than setting the app delegate, because otherwise our handlers
 
  54     // get overwritten in the meantime
 
  56     NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
 
  58     [appleEventManager setEventHandler:self andSelector:@selector(handleGetURLEvent:withReplyEvent:)
 
  59                          forEventClass:kInternetEventClass andEventID:kAEGetURL];
 
  61     [appleEventManager setEventHandler:self andSelector:@selector(handleOpenAppEvent:withReplyEvent:)
 
  62                          forEventClass:kCoreEventClass andEventID:kAEOpenApplication];
 
  64     wxTheApp->OSXOnWillFinishLaunching();
 
  67 - (void)applicationDidFinishLaunching:(NSNotification *)notification
 
  69     wxTheApp->OSXOnDidFinishLaunching();
 
  72 - (void)application:(NSApplication *)sender openFiles:(NSArray *)fileNames
 
  75     wxArrayString fileList;
 
  77     const size_t count = [fileNames count];
 
  78     for (i = 0; i < count; i++)
 
  80         fileList.Add( wxCFStringRef::AsStringWithNormalizationFormC([fileNames objectAtIndex:i]) );
 
  83     wxTheApp->MacOpenFiles(fileList);
 
  86 - (BOOL)application:(NSApplication *)sender printFile:(NSString *)filename
 
  89     wxCFStringRef cf(wxCFRetain(filename));
 
  90     wxTheApp->MacPrintFile(cf.AsString()) ;
 
  94 - (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag
 
  98     wxTheApp->MacReopenApp() ;
 
 102 - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event
 
 103            withReplyEvent:(NSAppleEventDescriptor *)replyEvent
 
 105     wxUnusedVar(replyEvent);
 
 106     NSString* url = [[event descriptorAtIndex:1] stringValue];
 
 107     wxCFStringRef cf(wxCFRetain(url));
 
 108     wxTheApp->MacOpenURL(cf.AsString()) ;
 
 111 - (void)handleOpenAppEvent:(NSAppleEventDescriptor *)event
 
 112            withReplyEvent:(NSAppleEventDescriptor *)replyEvent
 
 114     wxUnusedVar(replyEvent);
 
 115     wxTheApp->MacNewFile() ;
 
 119     Allowable return values are:
 
 120         NSTerminateNow - it is ok to proceed with termination
 
 121         NSTerminateCancel - the application should not be terminated
 
 122         NSTerminateLater - it may be ok to proceed with termination later.  The application must call -replyToApplicationShouldTerminate: with YES or NO once the answer is known
 
 123             this return value is for delegates who need to provide document modal alerts (sheets) in order to decide whether to quit.
 
 125 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
 
 128     if ( !wxTheApp->OSXOnShouldTerminate() )
 
 129         return NSTerminateCancel;
 
 131     return NSTerminateNow;
 
 134 - (void)applicationWillTerminate:(NSNotification *)application {
 
 135     wxUnusedVar(application);
 
 136     wxTheApp->OSXOnWillTerminate();
 
 139 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
 
 142     // let wx do this, not cocoa
 
 146 - (void)applicationDidBecomeActive:(NSNotification *)notification
 
 148     wxUnusedVar(notification);
 
 150     for ( wxWindowList::const_iterator i = wxTopLevelWindows.begin(),
 
 151          end = wxTopLevelWindows.end();
 
 155         wxTopLevelWindow * const win = static_cast<wxTopLevelWindow *>(*i);
 
 156         wxNonOwnedWindowImpl* winimpl = win ? win->GetNonOwnedPeer() : NULL;
 
 157         WXWindow nswindow = win ? win->GetWXWindow() : nil;
 
 159         if ( nswindow && [nswindow hidesOnDeactivate] == NO && winimpl)
 
 160             winimpl->RestoreWindowLevel();
 
 163         wxTheApp->SetActive( true , NULL ) ;
 
 166 - (void)applicationWillResignActive:(NSNotification *)notification
 
 168     wxUnusedVar(notification);
 
 169     for ( wxWindowList::const_iterator i = wxTopLevelWindows.begin(),
 
 170          end = wxTopLevelWindows.end();
 
 174         wxTopLevelWindow * const win = static_cast<wxTopLevelWindow *>(*i);
 
 175         WXWindow nswindow = win ? win->GetWXWindow() : nil;
 
 177         if ( nswindow && [nswindow level] == kCGFloatingWindowLevel && [nswindow hidesOnDeactivate] == NO )
 
 178             [nswindow setLevel:kCGNormalWindowLevel];
 
 182 - (void)applicationDidResignActive:(NSNotification *)notification
 
 184     wxUnusedVar(notification);
 
 186         wxTheApp->SetActive( false , NULL ) ;
 
 192     allows ShowModal to work when using sheets.
 
 193     see include/wx/osx/cocoa/private.h for more info
 
 195 @implementation ModalDialogDelegate
 
 205 - (void)setImplementation: (wxDialog *)dialog
 
 212     return sheetFinished;
 
 220 - (void)waitForSheetToFinish
 
 222     while (!sheetFinished)
 
 228 - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
 
 230     wxUnusedVar(contextInfo);
 
 231     resultCode = returnCode;
 
 233     // NSAlerts don't need nor respond to orderOut
 
 234     if ([sheet respondsToSelector:@selector(orderOut:)])
 
 235         [sheet orderOut: self];
 
 238         impl->ModalFinishedCallback(sheet, returnCode);
 
 242 // here we subclass NSApplication, for the purpose of being able to override sendEvent.
 
 243 @interface wxNSApplication : NSApplication
 
 250 - (void)sendEvent:(NSEvent *)anEvent;
 
 254 @implementation wxNSApplication
 
 263 /* This is needed because otherwise we don't receive any key-up events for command-key
 
 264  combinations (an AppKit bug, apparently)                       */
 
 265 - (void)sendEvent:(NSEvent *)anEvent
 
 267     if ([anEvent type] == NSKeyUp && ([anEvent modifierFlags] & NSCommandKeyMask))
 
 268         [[self keyWindow] sendEvent:anEvent];
 
 270         [super sendEvent:anEvent];
 
 282 WX_NSObject appcontroller = nil;
 
 284 NSLayoutManager* gNSLayoutManager = nil;
 
 286 WX_NSObject wxApp::OSXCreateAppController()
 
 288     return [[wxNSAppController alloc] init];
 
 291 bool wxApp::DoInitGui()
 
 293     wxMacAutoreleasePool pool;
 
 297         [wxNSApplication sharedApplication];
 
 299         appcontroller = OSXCreateAppController();
 
 300         [NSApp setDelegate:appcontroller];
 
 301         [NSColor setIgnoresAlpha:NO];
 
 303         // calling finishLaunching so early before running the loop seems to trigger some 'MenuManager compatibility' which leads
 
 304         // to the duplication of menus under 10.5 and a warning under 10.6
 
 306         [NSApp finishLaunching];
 
 309     gNSLayoutManager = [[NSLayoutManager alloc] init];
 
 314 bool wxApp::CallOnInit()
 
 316     wxMacAutoreleasePool autoreleasepool;
 
 317     // this will only run one cycle to make sure the OS is ready
 
 322 void wxApp::DoCleanUp()
 
 324     if ( appcontroller != nil )
 
 326         [NSApp setDelegate:nil];
 
 327         [appcontroller release];
 
 330     if ( gNSLayoutManager != nil )
 
 332         [gNSLayoutManager release];
 
 333         gNSLayoutManager = nil;
 
 337 void wxClientDisplayRect(int *x, int *y, int *width, int *height)
 
 339     NSRect displayRect = [wxOSXGetMenuScreen() visibleFrame];
 
 340     wxRect r = wxFromNSRect( NULL, displayRect );
 
 346         *width = r.GetWidth();
 
 348         *height = r.GetHeight();
 
 352 void wxGetMousePosition( int* x, int* y )
 
 354     wxPoint pt = wxFromNSPoint(NULL, [NSEvent mouseLocation]);
 
 361 #if wxOSX_USE_COCOA && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
 
 363 wxMouseState wxGetMouseState()
 
 367     wxPoint pt = wxGetMousePosition();
 
 371     NSUInteger modifiers = [NSEvent modifierFlags];
 
 372     NSUInteger buttons = [NSEvent pressedMouseButtons];
 
 374     ms.SetLeftDown( (buttons & 0x01) != 0 );
 
 375     ms.SetMiddleDown( (buttons & 0x04) != 0 );
 
 376     ms.SetRightDown( (buttons & 0x02) != 0 );
 
 378     ms.SetRawControlDown(modifiers & NSControlKeyMask);
 
 379     ms.SetShiftDown(modifiers & NSShiftKeyMask);
 
 380     ms.SetAltDown(modifiers & NSAlternateKeyMask);
 
 381     ms.SetControlDown(modifiers & NSCommandKeyMask);
 
 389 wxTimerImpl* wxGUIAppTraits::CreateTimerImpl(wxTimer *timer)
 
 391     return new wxOSXTimerImpl(timer);
 
 394 int gs_wxBusyCursorCount = 0;
 
 395 extern wxCursor    gMacCurrentCursor;
 
 396 wxCursor        gMacStoredActiveCursor;
 
 398 // Set the cursor to the busy cursor for all windows
 
 399 void wxBeginBusyCursor(const wxCursor *cursor)
 
 401     if (gs_wxBusyCursorCount++ == 0)
 
 403         NSEnumerator *enumerator = [[[NSApplication sharedApplication] windows] objectEnumerator];
 
 406         while ((object = [enumerator nextObject])) {
 
 407             [(NSWindow*) object disableCursorRects];
 
 410         gMacStoredActiveCursor = gMacCurrentCursor;
 
 411         cursor->MacInstall();
 
 413         wxSetCursor(*cursor);
 
 415     //else: nothing to do, already set
 
 418 // Restore cursor to normal
 
 419 void wxEndBusyCursor()
 
 421     wxCHECK_RET( gs_wxBusyCursorCount > 0,
 
 422         wxT("no matching wxBeginBusyCursor() for wxEndBusyCursor()") );
 
 424     if (--gs_wxBusyCursorCount == 0)
 
 426         NSEnumerator *enumerator = [[[NSApplication sharedApplication] windows] objectEnumerator];
 
 429         while ((object = [enumerator nextObject])) {
 
 430             [(NSWindow*) object enableCursorRects];
 
 433         wxSetCursor(wxNullCursor);
 
 435         gMacStoredActiveCursor.MacInstall();
 
 436         gMacStoredActiveCursor = wxNullCursor;
 
 440 // true if we're between the above two calls
 
 443     return (gs_wxBusyCursorCount > 0);
 
 446 wxBitmap wxWindowDCImpl::DoGetAsBitmap(const wxRect *subrect) const
 
 448     // wxScreenDC is derived from wxWindowDC, so a screen dc will
 
 449     // call this method when a Blit is performed with it as a source.
 
 453     wxSize sz = m_window->GetSize();
 
 455     int width = subrect != NULL ? subrect->width : sz.x;
 
 456     int height = subrect !=  NULL ? subrect->height : sz.y ;
 
 458     wxBitmap bitmap(width, height);
 
 460     NSView* view = (NSView*) m_window->GetHandle();
 
 461     if ( [view isHiddenOrHasHiddenAncestor] == NO )
 
 464         // we use this method as other methods force a repaint, and this method can be
 
 465         // called from OnPaint, even with the window's paint dc as source (see wxHTMLWindow)
 
 466         NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect: [view bounds]];
 
 468         if ( [rep respondsToSelector:@selector(CGImage)] )
 
 470             CGImageRef cgImageRef = (CGImageRef)[rep CGImage];
 
 472             CGRect r = CGRectMake( 0 , 0 , CGImageGetWidth(cgImageRef)  , CGImageGetHeight(cgImageRef) );
 
 473             // since our context is upside down we dont use CGContextDrawImage
 
 474             wxMacDrawCGImage( (CGContextRef) bitmap.GetHBITMAP() , &r, cgImageRef ) ;
 
 478             // TODO for 10.4 in case we can support this for osx_cocoa
 
 488 #endif // wxOSX_USE_COCOA