]> git.saurik.com Git - wxWidgets.git/blob - src/osx/cocoa/utils.mm
fbed3e18567ec96d81e08605880dd58e25a95b1f
[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 }
246
247 - (void)sendEvent:(NSEvent *)anEvent;
248
249 @end
250
251 @implementation wxNSApplication
252
253 /* This is needed because otherwise we don't receive any key-up events for command-key
254 combinations (an AppKit bug, apparently) */
255 - (void)sendEvent:(NSEvent *)anEvent
256 {
257 if ([anEvent type] == NSKeyUp && ([anEvent modifierFlags] & NSCommandKeyMask))
258 [[self keyWindow] sendEvent:anEvent];
259 else [super sendEvent:anEvent];
260 }
261
262 @end
263
264 WX_NSObject appcontroller = nil;
265
266 NSLayoutManager* gNSLayoutManager = nil;
267
268 WX_NSObject wxApp::OSXCreateAppController()
269 {
270 return [[wxNSAppController alloc] init];
271 }
272
273 bool wxApp::DoInitGui()
274 {
275 wxMacAutoreleasePool pool;
276
277 if (!sm_isEmbedded)
278 {
279 [wxNSApplication sharedApplication];
280
281 appcontroller = OSXCreateAppController();
282 [NSApp setDelegate:appcontroller];
283 [NSColor setIgnoresAlpha:NO];
284
285 // calling finishLaunching so early before running the loop seems to trigger some 'MenuManager compatibility' which leads
286 // to the duplication of menus under 10.5 and a warning under 10.6
287 #if 0
288 [NSApp finishLaunching];
289 #endif
290 }
291 gNSLayoutManager = [[NSLayoutManager alloc] init];
292
293 return true;
294 }
295
296 void wxApp::DoCleanUp()
297 {
298 if ( appcontroller != nil )
299 {
300 [NSApp setDelegate:nil];
301 [appcontroller release];
302 appcontroller = nil;
303 }
304 if ( gNSLayoutManager != nil )
305 {
306 [gNSLayoutManager release];
307 gNSLayoutManager = nil;
308 }
309 }
310
311 void wxClientDisplayRect(int *x, int *y, int *width, int *height)
312 {
313 NSRect displayRect = [wxOSXGetMenuScreen() visibleFrame];
314 wxRect r = wxFromNSRect( NULL, displayRect );
315 if ( x )
316 *x = r.x;
317 if ( y )
318 *y = r.y;
319 if ( width )
320 *width = r.GetWidth();
321 if ( height )
322 *height = r.GetHeight();
323
324 }
325
326 void wxGetMousePosition( int* x, int* y )
327 {
328 wxPoint pt = wxFromNSPoint(NULL, [NSEvent mouseLocation]);
329 if ( x )
330 *x = pt.x;
331 if ( y )
332 *y = pt.y;
333 };
334
335 #if wxOSX_USE_COCOA && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
336
337 wxMouseState wxGetMouseState()
338 {
339 wxMouseState ms;
340
341 wxPoint pt = wxGetMousePosition();
342 ms.SetX(pt.x);
343 ms.SetY(pt.y);
344
345 NSUInteger modifiers = [NSEvent modifierFlags];
346 NSUInteger buttons = [NSEvent pressedMouseButtons];
347
348 ms.SetLeftDown( (buttons & 0x01) != 0 );
349 ms.SetMiddleDown( (buttons & 0x04) != 0 );
350 ms.SetRightDown( (buttons & 0x02) != 0 );
351
352 ms.SetRawControlDown(modifiers & NSControlKeyMask);
353 ms.SetShiftDown(modifiers & NSShiftKeyMask);
354 ms.SetAltDown(modifiers & NSAlternateKeyMask);
355 ms.SetControlDown(modifiers & NSCommandKeyMask);
356
357 return ms;
358 }
359
360
361 #endif
362
363 wxTimerImpl* wxGUIAppTraits::CreateTimerImpl(wxTimer *timer)
364 {
365 return new wxOSXTimerImpl(timer);
366 }
367
368 int gs_wxBusyCursorCount = 0;
369 extern wxCursor gMacCurrentCursor;
370 wxCursor gMacStoredActiveCursor;
371
372 // Set the cursor to the busy cursor for all windows
373 void wxBeginBusyCursor(const wxCursor *cursor)
374 {
375 if (gs_wxBusyCursorCount++ == 0)
376 {
377 NSEnumerator *enumerator = [[[NSApplication sharedApplication] windows] objectEnumerator];
378 id object;
379
380 while ((object = [enumerator nextObject])) {
381 [(NSWindow*) object disableCursorRects];
382 }
383
384 gMacStoredActiveCursor = gMacCurrentCursor;
385 cursor->MacInstall();
386
387 wxSetCursor(*cursor);
388 }
389 //else: nothing to do, already set
390 }
391
392 // Restore cursor to normal
393 void wxEndBusyCursor()
394 {
395 wxCHECK_RET( gs_wxBusyCursorCount > 0,
396 wxT("no matching wxBeginBusyCursor() for wxEndBusyCursor()") );
397
398 if (--gs_wxBusyCursorCount == 0)
399 {
400 NSEnumerator *enumerator = [[[NSApplication sharedApplication] windows] objectEnumerator];
401 id object;
402
403 while ((object = [enumerator nextObject])) {
404 [(NSWindow*) object enableCursorRects];
405 }
406
407 wxSetCursor(wxNullCursor);
408
409 gMacStoredActiveCursor.MacInstall();
410 gMacStoredActiveCursor = wxNullCursor;
411 }
412 }
413
414 // true if we're between the above two calls
415 bool wxIsBusy()
416 {
417 return (gs_wxBusyCursorCount > 0);
418 }
419
420 wxBitmap wxWindowDCImpl::DoGetAsBitmap(const wxRect *subrect) const
421 {
422 // wxScreenDC is derived from wxWindowDC, so a screen dc will
423 // call this method when a Blit is performed with it as a source.
424 if (!m_window)
425 return wxNullBitmap;
426
427 wxSize sz = m_window->GetSize();
428
429 int width = subrect != NULL ? subrect->width : sz.x;
430 int height = subrect != NULL ? subrect->height : sz.y ;
431
432 wxBitmap bitmap(width, height);
433
434 NSView* view = (NSView*) m_window->GetHandle();
435 if ( [view isHiddenOrHasHiddenAncestor] == NO )
436 {
437 [view lockFocus];
438 // we use this method as other methods force a repaint, and this method can be
439 // called from OnPaint, even with the window's paint dc as source (see wxHTMLWindow)
440 NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect: [view bounds]];
441 [view unlockFocus];
442 if ( [rep respondsToSelector:@selector(CGImage)] )
443 {
444 CGImageRef cgImageRef = (CGImageRef)[rep CGImage];
445
446 CGRect r = CGRectMake( 0 , 0 , CGImageGetWidth(cgImageRef) , CGImageGetHeight(cgImageRef) );
447 // since our context is upside down we dont use CGContextDrawImage
448 wxMacDrawCGImage( (CGContextRef) bitmap.GetHBITMAP() , &r, cgImageRef ) ;
449 }
450 else
451 {
452 // TODO for 10.4 in case we can support this for osx_cocoa
453 }
454 [rep release];
455 }
456
457 return bitmap;
458 }
459
460 #endif // wxUSE_GUI
461
462 #endif // wxOSX_USE_COCOA