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