]> git.saurik.com Git - wxWidgets.git/blob - src/osx/cocoa/utils.mm
follow up parent chain to properly support modal dialog parents, see #15383
[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