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