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