]> git.saurik.com Git - wxWidgets.git/blob - src/osx/cocoa/utils.mm
77289eb9137deb81210858ff8f56d9a43e10bdf4
[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 if ( !wxTheApp->OSXOnShouldTerminate() )
130 return NSTerminateCancel;
131
132 return NSTerminateNow;
133 }
134
135 - (void)applicationWillTerminate:(NSNotification *)application {
136 wxUnusedVar(application);
137 wxTheApp->OSXOnWillTerminate();
138 }
139
140 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
141 {
142 wxUnusedVar(sender);
143 // let wx do this, not cocoa
144 return NO;
145 }
146
147 - (void)applicationDidBecomeActive:(NSNotification *)notification
148 {
149 wxUnusedVar(notification);
150
151 for ( wxWindowList::const_iterator i = wxTopLevelWindows.begin(),
152 end = wxTopLevelWindows.end();
153 i != end;
154 ++i )
155 {
156 wxTopLevelWindow * const win = static_cast<wxTopLevelWindow *>(*i);
157 wxNonOwnedWindowImpl* winimpl = win ? win->GetNonOwnedPeer() : NULL;
158 WXWindow nswindow = win ? win->GetWXWindow() : nil;
159
160 if ( nswindow && [nswindow hidesOnDeactivate] == NO && winimpl)
161 winimpl->RestoreWindowLevel();
162 }
163 if ( wxTheApp )
164 wxTheApp->SetActive( true , NULL ) ;
165 }
166
167 - (void)applicationWillResignActive:(NSNotification *)notification
168 {
169 wxUnusedVar(notification);
170 for ( wxWindowList::const_iterator i = wxTopLevelWindows.begin(),
171 end = wxTopLevelWindows.end();
172 i != end;
173 ++i )
174 {
175 wxTopLevelWindow * const win = static_cast<wxTopLevelWindow *>(*i);
176 WXWindow nswindow = win ? win->GetWXWindow() : nil;
177
178 if ( nswindow && [nswindow level] == kCGFloatingWindowLevel && [nswindow hidesOnDeactivate] == NO )
179 [nswindow setLevel:kCGNormalWindowLevel];
180 }
181 }
182
183 - (void)applicationDidResignActive:(NSNotification *)notification
184 {
185 wxUnusedVar(notification);
186 if ( wxTheApp )
187 wxTheApp->SetActive( false , NULL ) ;
188 }
189
190 @end
191
192 /*
193 allows ShowModal to work when using sheets.
194 see include/wx/osx/cocoa/private.h for more info
195 */
196 @implementation ModalDialogDelegate
197 - (id)init
198 {
199 self = [super init];
200 sheetFinished = NO;
201 resultCode = -1;
202 impl = 0;
203 return self;
204 }
205
206 - (void)setImplementation: (wxDialog *)dialog
207 {
208 impl = dialog;
209 }
210
211 - (BOOL)finished
212 {
213 return sheetFinished;
214 }
215
216 - (int)code
217 {
218 return resultCode;
219 }
220
221 - (void)waitForSheetToFinish
222 {
223 while (!sheetFinished)
224 {
225 wxSafeYield();
226 }
227 }
228
229 - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
230 {
231 wxUnusedVar(contextInfo);
232 resultCode = returnCode;
233 sheetFinished = YES;
234 // NSAlerts don't need nor respond to orderOut
235 if ([sheet respondsToSelector:@selector(orderOut:)])
236 [sheet orderOut: self];
237
238 if (impl)
239 impl->ModalFinishedCallback(sheet, returnCode);
240 }
241 @end
242
243 // here we subclass NSApplication, for the purpose of being able to override sendEvent.
244 @interface wxNSApplication : NSApplication
245 {
246 }
247
248 - (void)sendEvent:(NSEvent *)anEvent;
249
250 @end
251
252 @implementation wxNSApplication
253
254 /* This is needed because otherwise we don't receive any key-up events for command-key
255 combinations (an AppKit bug, apparently) */
256 - (void)sendEvent:(NSEvent *)anEvent
257 {
258 if ([anEvent type] == NSKeyUp && ([anEvent modifierFlags] & NSCommandKeyMask))
259 [[self keyWindow] sendEvent:anEvent];
260 else [super sendEvent:anEvent];
261 }
262
263 @end
264
265 WX_NSObject appcontroller = nil;
266
267 NSLayoutManager* gNSLayoutManager = nil;
268
269 WX_NSObject wxApp::OSXCreateAppController()
270 {
271 return [[wxNSAppController alloc] init];
272 }
273
274 bool wxApp::DoInitGui()
275 {
276 wxMacAutoreleasePool pool;
277
278 if (!sm_isEmbedded)
279 {
280 [wxNSApplication sharedApplication];
281
282 appcontroller = OSXCreateAppController();
283 [NSApp setDelegate:appcontroller];
284 [NSColor setIgnoresAlpha:NO];
285
286 // calling finishLaunching so early before running the loop seems to trigger some 'MenuManager compatibility' which leads
287 // to the duplication of menus under 10.5 and a warning under 10.6
288 #if 0
289 [NSApp finishLaunching];
290 #endif
291 }
292 gNSLayoutManager = [[NSLayoutManager alloc] init];
293
294 return true;
295 }
296
297 void wxApp::DoCleanUp()
298 {
299 if ( appcontroller != nil )
300 {
301 [NSApp setDelegate:nil];
302 [appcontroller release];
303 appcontroller = nil;
304 }
305 if ( gNSLayoutManager != nil )
306 {
307 [gNSLayoutManager release];
308 gNSLayoutManager = nil;
309 }
310 }
311
312 void wxClientDisplayRect(int *x, int *y, int *width, int *height)
313 {
314 NSRect displayRect = [wxOSXGetMenuScreen() visibleFrame];
315 wxRect r = wxFromNSRect( NULL, displayRect );
316 if ( x )
317 *x = r.x;
318 if ( y )
319 *y = r.y;
320 if ( width )
321 *width = r.GetWidth();
322 if ( height )
323 *height = r.GetHeight();
324
325 }
326
327 void wxGetMousePosition( int* x, int* y )
328 {
329 wxPoint pt = wxFromNSPoint(NULL, [NSEvent mouseLocation]);
330 if ( x )
331 *x = pt.x;
332 if ( y )
333 *y = pt.y;
334 };
335
336 #if wxOSX_USE_COCOA && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
337
338 wxMouseState wxGetMouseState()
339 {
340 wxMouseState ms;
341
342 wxPoint pt = wxGetMousePosition();
343 ms.SetX(pt.x);
344 ms.SetY(pt.y);
345
346 NSUInteger modifiers = [NSEvent modifierFlags];
347 NSUInteger buttons = [NSEvent pressedMouseButtons];
348
349 ms.SetLeftDown( (buttons & 0x01) != 0 );
350 ms.SetMiddleDown( (buttons & 0x04) != 0 );
351 ms.SetRightDown( (buttons & 0x02) != 0 );
352
353 ms.SetRawControlDown(modifiers & NSControlKeyMask);
354 ms.SetShiftDown(modifiers & NSShiftKeyMask);
355 ms.SetAltDown(modifiers & NSAlternateKeyMask);
356 ms.SetControlDown(modifiers & NSCommandKeyMask);
357
358 return ms;
359 }
360
361
362 #endif
363
364 wxTimerImpl* wxGUIAppTraits::CreateTimerImpl(wxTimer *timer)
365 {
366 return new wxOSXTimerImpl(timer);
367 }
368
369 int gs_wxBusyCursorCount = 0;
370 extern wxCursor gMacCurrentCursor;
371 wxCursor gMacStoredActiveCursor;
372
373 // Set the cursor to the busy cursor for all windows
374 void wxBeginBusyCursor(const wxCursor *cursor)
375 {
376 if (gs_wxBusyCursorCount++ == 0)
377 {
378 NSEnumerator *enumerator = [[[NSApplication sharedApplication] windows] objectEnumerator];
379 id object;
380
381 while ((object = [enumerator nextObject])) {
382 [(NSWindow*) object disableCursorRects];
383 }
384
385 gMacStoredActiveCursor = gMacCurrentCursor;
386 cursor->MacInstall();
387
388 wxSetCursor(*cursor);
389 }
390 //else: nothing to do, already set
391 }
392
393 // Restore cursor to normal
394 void wxEndBusyCursor()
395 {
396 wxCHECK_RET( gs_wxBusyCursorCount > 0,
397 wxT("no matching wxBeginBusyCursor() for wxEndBusyCursor()") );
398
399 if (--gs_wxBusyCursorCount == 0)
400 {
401 NSEnumerator *enumerator = [[[NSApplication sharedApplication] windows] objectEnumerator];
402 id object;
403
404 while ((object = [enumerator nextObject])) {
405 [(NSWindow*) object enableCursorRects];
406 }
407
408 wxSetCursor(wxNullCursor);
409
410 gMacStoredActiveCursor.MacInstall();
411 gMacStoredActiveCursor = wxNullCursor;
412 }
413 }
414
415 // true if we're between the above two calls
416 bool wxIsBusy()
417 {
418 return (gs_wxBusyCursorCount > 0);
419 }
420
421 wxBitmap wxWindowDCImpl::DoGetAsBitmap(const wxRect *subrect) const
422 {
423 // wxScreenDC is derived from wxWindowDC, so a screen dc will
424 // call this method when a Blit is performed with it as a source.
425 if (!m_window)
426 return wxNullBitmap;
427
428 wxSize sz = m_window->GetSize();
429
430 int width = subrect != NULL ? subrect->width : sz.x;
431 int height = subrect != NULL ? subrect->height : sz.y ;
432
433 wxBitmap bitmap(width, height);
434
435 NSView* view = (NSView*) m_window->GetHandle();
436 if ( [view isHiddenOrHasHiddenAncestor] == NO )
437 {
438 [view lockFocus];
439 // we use this method as other methods force a repaint, and this method can be
440 // called from OnPaint, even with the window's paint dc as source (see wxHTMLWindow)
441 NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect: [view bounds]];
442 [view unlockFocus];
443 if ( [rep respondsToSelector:@selector(CGImage)] )
444 {
445 CGImageRef cgImageRef = (CGImageRef)[rep CGImage];
446
447 CGRect r = CGRectMake( 0 , 0 , CGImageGetWidth(cgImageRef) , CGImageGetHeight(cgImageRef) );
448 // since our context is upside down we dont use CGContextDrawImage
449 wxMacDrawCGImage( (CGContextRef) bitmap.GetHBITMAP() , &r, cgImageRef ) ;
450 }
451 else
452 {
453 // TODO for 10.4 in case we can support this for osx_cocoa
454 }
455 [rep release];
456 }
457
458 return bitmap;
459 }
460
461 #endif // wxUSE_GUI
462
463 #endif // wxOSX_USE_COCOA