OSDN Git Service

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