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