Fix crash when auto-sizing a wxDataViewCtrl column.
[wxWidgets.git] / src / osx / cocoa / utils.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/osx/cocoa/utils.mm
3 // Purpose:     various cocoa utility functions
4 // Author:      Stefan Csomor
5 // Modified by:
6 // Created:     1998-01-01
7 // Copyright:   (c) Stefan Csomor
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 #include "wx/wxprec.h"
12
13 #include "wx/utils.h"
14
15 #ifndef WX_PRECOMP
16     #include "wx/intl.h"
17     #include "wx/app.h"
18     #if wxUSE_GUI
19         #include "wx/dialog.h"
20         #include "wx/toplevel.h"
21         #include "wx/font.h"
22     #endif
23 #endif
24
25 #include "wx/apptrait.h"
26
27 #include "wx/osx/private.h"
28
29 #if wxUSE_GUI
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"
34 #endif
35 #endif // wxUSE_GUI
36
37 #if wxOSX_USE_COCOA
38
39 #if wxUSE_GUI
40
41 // Emit a beeeeeep
42 void wxBell()
43 {
44     NSBeep();
45 }
46
47 @implementation wxNSAppController
48
49 - (void)applicationWillFinishLaunching:(NSNotification *)application
50 {
51     wxUnusedVar(application);
52     
53     // we must install our handlers later than setting the app delegate, because otherwise our handlers
54     // get overwritten in the meantime
55
56     NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
57     
58     [appleEventManager setEventHandler:self andSelector:@selector(handleGetURLEvent:withReplyEvent:)
59                          forEventClass:kInternetEventClass andEventID:kAEGetURL];
60     
61     [appleEventManager setEventHandler:self andSelector:@selector(handleOpenAppEvent:withReplyEvent:)
62                          forEventClass:kCoreEventClass andEventID:kAEOpenApplication];
63     
64     wxTheApp->OSXOnWillFinishLaunching();
65 }
66
67 - (void)applicationDidFinishLaunching:(NSNotification *)notification
68 {
69     wxTheApp->OSXOnDidFinishLaunching();
70 }
71
72 - (void)application:(NSApplication *)sender openFiles:(NSArray *)fileNames
73 {
74     wxUnusedVar(sender);
75     wxArrayString fileList;
76     size_t i;
77     const size_t count = [fileNames count];
78     for (i = 0; i < count; i++)
79     {
80         fileList.Add( wxCFStringRef::AsStringWithNormalizationFormC([fileNames objectAtIndex:i]) );
81     }
82
83     wxTheApp->MacOpenFiles(fileList);
84 }
85
86 - (BOOL)application:(NSApplication *)sender printFile:(NSString *)filename
87 {
88     wxUnusedVar(sender);
89     wxCFStringRef cf(wxCFRetain(filename));
90     wxTheApp->MacPrintFile(cf.AsString()) ;
91     return YES;
92 }
93
94 - (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag
95 {
96     wxUnusedVar(flag);
97     wxUnusedVar(sender);
98     wxTheApp->MacReopenApp() ;
99     return NO;
100 }
101
102 - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event
103            withReplyEvent:(NSAppleEventDescriptor *)replyEvent
104 {
105     wxUnusedVar(replyEvent);
106     NSString* url = [[event descriptorAtIndex:1] stringValue];
107     wxCFStringRef cf(wxCFRetain(url));
108     wxTheApp->MacOpenURL(cf.AsString()) ;
109 }
110
111 - (void)handleOpenAppEvent:(NSAppleEventDescriptor *)event
112            withReplyEvent:(NSAppleEventDescriptor *)replyEvent
113 {
114     wxUnusedVar(replyEvent);
115     wxTheApp->MacNewFile() ;
116 }
117
118 /*
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.
124 */
125 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
126 {
127     wxUnusedVar(sender);
128     if ( !wxTheApp->OSXOnShouldTerminate() )
129         return NSTerminateCancel;
130     
131     return NSTerminateNow;
132 }
133
134 - (void)applicationWillTerminate:(NSNotification *)application {
135     wxUnusedVar(application);
136     wxTheApp->OSXOnWillTerminate();
137 }
138
139 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
140 {
141     wxUnusedVar(sender);
142     // let wx do this, not cocoa
143     return NO;
144 }
145
146 - (void)applicationDidBecomeActive:(NSNotification *)notification
147 {
148     wxUnusedVar(notification);
149
150     for ( wxWindowList::const_iterator i = wxTopLevelWindows.begin(),
151          end = wxTopLevelWindows.end();
152          i != end;
153          ++i )
154     {
155         wxTopLevelWindow * const win = static_cast<wxTopLevelWindow *>(*i);
156         wxNonOwnedWindowImpl* winimpl = win ? win->GetNonOwnedPeer() : NULL;
157         WXWindow nswindow = win ? win->GetWXWindow() : nil;
158         
159         if ( nswindow && [nswindow hidesOnDeactivate] == NO && winimpl)
160             winimpl->RestoreWindowLevel();
161     }
162     if ( wxTheApp )
163         wxTheApp->SetActive( true , NULL ) ;
164 }
165
166 - (void)applicationWillResignActive:(NSNotification *)notification
167 {
168     wxUnusedVar(notification);
169     for ( wxWindowList::const_iterator i = wxTopLevelWindows.begin(),
170          end = wxTopLevelWindows.end();
171          i != end;
172          ++i )
173     {
174         wxTopLevelWindow * const win = static_cast<wxTopLevelWindow *>(*i);
175         WXWindow nswindow = win ? win->GetWXWindow() : nil;
176         
177         if ( nswindow && [nswindow level] == kCGFloatingWindowLevel && [nswindow hidesOnDeactivate] == NO )
178             [nswindow setLevel:kCGNormalWindowLevel];
179     }
180 }
181
182 - (void)applicationDidResignActive:(NSNotification *)notification
183 {
184     wxUnusedVar(notification);
185     if ( wxTheApp )
186         wxTheApp->SetActive( false , NULL ) ;
187 }
188
189 @end
190
191 /*
192     allows ShowModal to work when using sheets.
193     see include/wx/osx/cocoa/private.h for more info
194 */
195 @implementation ModalDialogDelegate
196 - (id)init
197 {
198     self = [super init];
199     sheetFinished = NO;
200     resultCode = -1;
201     impl = 0;
202     return self;
203 }
204
205 - (void)setImplementation: (wxDialog *)dialog
206 {
207     impl = dialog;
208 }
209
210 - (BOOL)finished
211 {
212     return sheetFinished;
213 }
214
215 - (int)code
216 {
217     return resultCode;
218 }
219
220 - (void)waitForSheetToFinish
221 {
222     while (!sheetFinished)
223     {
224         wxSafeYield();
225     }
226 }
227
228 - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
229 {
230     wxUnusedVar(contextInfo);
231     resultCode = returnCode;
232     sheetFinished = YES;
233     // NSAlerts don't need nor respond to orderOut
234     if ([sheet respondsToSelector:@selector(orderOut:)])
235         [sheet orderOut: self];
236         
237     if (impl)
238         impl->ModalFinishedCallback(sheet, returnCode);
239 }
240 @end
241
242 // here we subclass NSApplication, for the purpose of being able to override sendEvent.
243 @interface wxNSApplication : NSApplication
244 {
245     BOOL firstPass;
246 }
247
248 - (id)init;
249
250 - (void)sendEvent:(NSEvent *)anEvent;
251
252 @end
253
254 @implementation wxNSApplication
255
256 - (id)init
257 {
258     self = [super init];
259     firstPass = YES;
260     return self;
261 }
262
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
266 {
267     if ([anEvent type] == NSKeyUp && ([anEvent modifierFlags] & NSCommandKeyMask))
268         [[self keyWindow] sendEvent:anEvent];
269     else
270         [super sendEvent:anEvent];
271     
272     if ( firstPass )
273     {
274         [NSApp stop:nil];
275         firstPass = NO;
276         return;
277     }
278 }
279
280 @end
281
282 WX_NSObject appcontroller = nil;
283
284 NSLayoutManager* gNSLayoutManager = nil;
285
286 WX_NSObject wxApp::OSXCreateAppController()
287 {
288     return [[wxNSAppController alloc] init];
289 }
290
291 bool wxApp::DoInitGui()
292 {
293     wxMacAutoreleasePool pool;
294
295     if (!sm_isEmbedded)
296     {
297         [wxNSApplication sharedApplication];
298
299         appcontroller = OSXCreateAppController();
300         [NSApp setDelegate:appcontroller];
301         [NSColor setIgnoresAlpha:NO];
302
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
305 #if 0
306         [NSApp finishLaunching];
307 #endif
308     }
309     gNSLayoutManager = [[NSLayoutManager alloc] init];
310     
311     return true;
312 }
313
314 bool wxApp::CallOnInit()
315 {
316     wxMacAutoreleasePool autoreleasepool;
317     m_onInitResult = false;
318     [NSApp run];
319     return m_onInitResult; 
320 }
321
322 void wxApp::DoCleanUp()
323 {
324     if ( appcontroller != nil )
325     {
326         [NSApp setDelegate:nil];
327         [appcontroller release];
328         appcontroller = nil;
329     }
330     if ( gNSLayoutManager != nil )
331     {
332         [gNSLayoutManager release];
333         gNSLayoutManager = nil;
334     }
335 }
336
337 void wxClientDisplayRect(int *x, int *y, int *width, int *height)
338 {
339     NSRect displayRect = [wxOSXGetMenuScreen() visibleFrame];
340     wxRect r = wxFromNSRect( NULL, displayRect );
341     if ( x )
342         *x = r.x;
343     if ( y )
344         *y = r.y;
345     if ( width )
346         *width = r.GetWidth();
347     if ( height )
348         *height = r.GetHeight();
349
350 }
351
352 void wxGetMousePosition( int* x, int* y )
353 {
354     wxPoint pt = wxFromNSPoint(NULL, [NSEvent mouseLocation]);
355     if ( x )
356         *x = pt.x;
357     if ( y )
358         *y = pt.y;
359 };
360
361 #if wxOSX_USE_COCOA && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
362
363 wxMouseState wxGetMouseState()
364 {
365     wxMouseState ms;
366     
367     wxPoint pt = wxGetMousePosition();
368     ms.SetX(pt.x);
369     ms.SetY(pt.y);
370     
371     NSUInteger modifiers = [NSEvent modifierFlags];
372     NSUInteger buttons = [NSEvent pressedMouseButtons];
373     
374     ms.SetLeftDown( (buttons & 0x01) != 0 );
375     ms.SetMiddleDown( (buttons & 0x04) != 0 );
376     ms.SetRightDown( (buttons & 0x02) != 0 );
377     
378     ms.SetRawControlDown(modifiers & NSControlKeyMask);
379     ms.SetShiftDown(modifiers & NSShiftKeyMask);
380     ms.SetAltDown(modifiers & NSAlternateKeyMask);
381     ms.SetControlDown(modifiers & NSCommandKeyMask);
382     
383     return ms;
384 }
385
386
387 #endif
388
389 wxTimerImpl* wxGUIAppTraits::CreateTimerImpl(wxTimer *timer)
390 {
391     return new wxOSXTimerImpl(timer);
392 }
393
394 int gs_wxBusyCursorCount = 0;
395 extern wxCursor    gMacCurrentCursor;
396 wxCursor        gMacStoredActiveCursor;
397
398 // Set the cursor to the busy cursor for all windows
399 void wxBeginBusyCursor(const wxCursor *cursor)
400 {
401     if (gs_wxBusyCursorCount++ == 0)
402     {
403         NSEnumerator *enumerator = [[[NSApplication sharedApplication] windows] objectEnumerator];
404         id object;
405         
406         while ((object = [enumerator nextObject])) {
407             [(NSWindow*) object disableCursorRects];
408         }        
409
410         gMacStoredActiveCursor = gMacCurrentCursor;
411         cursor->MacInstall();
412
413         wxSetCursor(*cursor);
414     }
415     //else: nothing to do, already set
416 }
417
418 // Restore cursor to normal
419 void wxEndBusyCursor()
420 {
421     wxCHECK_RET( gs_wxBusyCursorCount > 0,
422         wxT("no matching wxBeginBusyCursor() for wxEndBusyCursor()") );
423
424     if (--gs_wxBusyCursorCount == 0)
425     {
426         NSEnumerator *enumerator = [[[NSApplication sharedApplication] windows] objectEnumerator];
427         id object;
428         
429         while ((object = [enumerator nextObject])) {
430             [(NSWindow*) object enableCursorRects];
431         }        
432
433         wxSetCursor(wxNullCursor);
434
435         gMacStoredActiveCursor.MacInstall();
436         gMacStoredActiveCursor = wxNullCursor;
437     }
438 }
439
440 // true if we're between the above two calls
441 bool wxIsBusy()
442 {
443     return (gs_wxBusyCursorCount > 0);
444 }
445
446 wxBitmap wxWindowDCImpl::DoGetAsBitmap(const wxRect *subrect) const
447 {
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.
450     if (!m_window)
451         return wxNullBitmap;
452
453     wxSize sz = m_window->GetSize();
454
455     int width = subrect != NULL ? subrect->width : sz.x;
456     int height = subrect !=  NULL ? subrect->height : sz.y ;
457
458     wxBitmap bitmap(width, height);
459
460     NSView* view = (NSView*) m_window->GetHandle();
461     if ( [view isHiddenOrHasHiddenAncestor] == NO )
462     {
463         [view lockFocus];
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]];
467         [view unlockFocus];
468         if ( [rep respondsToSelector:@selector(CGImage)] )
469         {
470             CGImageRef cgImageRef = (CGImageRef)[rep CGImage];
471
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 ) ;
475         }
476         else
477         {
478             // TODO for 10.4 in case we can support this for osx_cocoa
479         }
480         [rep release];
481     }
482
483     return bitmap;
484 }
485
486 #endif // wxUSE_GUI
487
488 #endif // wxOSX_USE_COCOA