1/*
2 * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#import "ApplicationDelegate.h"
27
28#import "com_apple_eawt_Application.h"
29#import "com_apple_eawt__AppDockIconHandler.h"
30#import "com_apple_eawt__AppEventHandler.h"
31#import "com_apple_eawt__AppMenuBarHandler.h"
32#import "com_apple_eawt__AppMenuBarHandler.h"
33#import "com_apple_eawt__AppMiscHandlers.h"
34
35#import <JavaNativeFoundation/JavaNativeFoundation.h>
36
37#import "CPopupMenu.h"
38#import "ThreadUtilities.h"
39#import "NSApplicationAWT.h"
40
41
42#pragma mark App Menu helpers
43
44// The following is a AWT convention?
45#define PREFERENCES_TAG  42
46
47static void addMenuItem(NSMenuItem* menuItem, NSInteger index) {
48AWT_ASSERT_APPKIT_THREAD;
49
50    NSMenu *menuBar = [[NSApplication sharedApplication] mainMenu];
51    NSMenu *appMenu = [[menuBar itemAtIndex:0] submenu];
52
53    [appMenu insertItem:menuItem atIndex:index];
54    [appMenu insertItem:[NSMenuItem separatorItem] atIndex:index + 1]; // Add the following separator
55}
56
57static void removeMenuItem(NSMenuItem* menuItem) {
58AWT_ASSERT_APPKIT_THREAD;
59
60    NSMenu *menuBar = [[NSApplication sharedApplication] mainMenu];
61    NSMenu *appMenu = [[menuBar itemAtIndex:0] submenu];
62
63    NSInteger index = [appMenu indexOfItem:menuItem];
64    if (index < 0) return; // something went wrong
65
66    [appMenu removeItemAtIndex:index + 1]; // Get the following separator
67    [appMenu removeItem:menuItem];
68}
69
70@interface NSBundle (EAWTOverrides)
71- (BOOL)_hasEAWTOverride:(NSString *)key;
72@end
73
74
75@implementation NSBundle (EAWTOverrides)
76
77- (BOOL)_hasEAWTOverride:(NSString *)key {
78    return [[[[self objectForInfoDictionaryKey:@"Java"] objectForKey:@"EAWTOverride"] objectForKey:key] boolValue];
79}
80
81@end
82
83
84// used by JavaRuntimeSupport.framework's [JRSAppKitAWT awtAppDelegate]
85// to expose our app delegate to the SWT or other apps that have knoledge
86// of Java's AWT and want to install their own app delegate that will delegate
87// to the AWT for some operations
88
89@interface JavaAWTAppDelegateLoader : NSObject { }
90@end
91
92@implementation JavaAWTAppDelegateLoader
93+ (ApplicationDelegate *) awtAppDelegate {
94    return [ApplicationDelegate sharedDelegate];
95}
96@end
97
98
99@implementation ApplicationDelegate
100
101@synthesize fPreferencesMenu;
102@synthesize fAboutMenu;
103@synthesize fProgressIndicator;
104
105@synthesize fDockMenu;
106@synthesize fDefaultMenuBar;
107
108
109+ (ApplicationDelegate *)sharedDelegate {
110    static ApplicationDelegate *sApplicationDelegate = nil;
111    static BOOL checked = NO;
112
113    if (sApplicationDelegate != nil) return sApplicationDelegate;
114    if (checked) return nil;
115
116AWT_ASSERT_APPKIT_THREAD;
117
118    // don't install the EAWT delegate if another kind of NSApplication is installed, like say, Safari
119    BOOL shouldInstall = NO;
120    if (NSApp != nil) {
121        if ([NSApp isMemberOfClass:[NSApplication class]]) shouldInstall = YES;
122        if ([NSApp isKindOfClass:[NSApplicationAWT class]]) shouldInstall = YES;
123    }
124    checked = YES;
125    if (!shouldInstall) return nil;
126
127    sApplicationDelegate = [[ApplicationDelegate alloc] init];
128    return sApplicationDelegate;
129}
130
131- (void)_updatePreferencesMenu:(BOOL)prefsAvailable enabled:(BOOL)prefsEnabled {
132AWT_ASSERT_APPKIT_THREAD;
133
134    if (prefsAvailable) {
135        // Make sure Prefs is around
136        if ([self.fPreferencesMenu menu] == nil) {
137            // Position of Prefs depends upon About availability.
138            NSInteger index = ([self.fAboutMenu menu] != nil) ? 2 : 0;
139
140            addMenuItem(self.fPreferencesMenu, index);
141        }
142
143        if (prefsEnabled) {
144            [self.fPreferencesMenu setEnabled:YES];
145            [self.fPreferencesMenu setTarget:self];
146            [self.fPreferencesMenu setAction:@selector(_preferencesMenuHandler)];
147        } else {
148            [self.fPreferencesMenu setEnabled:NO];
149            [self.fPreferencesMenu setTarget:nil];
150            [self.fPreferencesMenu setAction:nil];
151        }
152    } else {
153        if ([self.fPreferencesMenu menu] == nil) return;
154
155        // Remove the preferences item
156        removeMenuItem(self.fPreferencesMenu);
157    }
158}
159
160- (void)_updateAboutMenu:(BOOL)aboutAvailable enabled:(BOOL)aboutEnabled {
161AWT_ASSERT_APPKIT_THREAD;
162
163    if (aboutAvailable) {
164        // Make sure About is around
165        if ([self.fAboutMenu menu] == nil) {
166            addMenuItem(self.fAboutMenu, 0);
167        }
168
169        if (aboutEnabled) {
170            [self.fAboutMenu setEnabled:YES];
171            [self.fAboutMenu setTarget:self];
172            [self.fAboutMenu setAction:@selector(_aboutMenuHandler)];
173        } else {
174            [self.fAboutMenu setEnabled:NO];
175            [self.fAboutMenu setTarget:nil];
176            [self.fAboutMenu setAction:nil];
177        }
178    } else {
179        if ([self.fAboutMenu menu] == nil) return;
180
181        // Remove About item.
182        removeMenuItem(self.fAboutMenu);
183    }
184}
185
186- (id) init {
187AWT_ASSERT_APPKIT_THREAD;
188
189    self = [super init];
190    if (!self) return self;
191
192    // Prep for about and preferences menu
193    BOOL usingDefaultNib = YES;
194    if ([NSApp isKindOfClass:[NSApplicationAWT class]]) {
195        usingDefaultNib = [NSApp usingDefaultNib];
196    }
197    if (!usingDefaultNib) return self;
198
199    NSMenu *menuBar = [[NSApplication sharedApplication] mainMenu];
200    NSMenu *appMenu = [[menuBar itemAtIndex:0] submenu];
201
202    self.fPreferencesMenu = (NSMenuItem*)[appMenu itemWithTag:PREFERENCES_TAG];
203    self.fAboutMenu = (NSMenuItem*)[appMenu itemAtIndex:0];
204    
205    NSDockTile *dockTile = [NSApp dockTile];
206    self.fProgressIndicator = [[NSProgressIndicator alloc]
207                                initWithFrame:NSMakeRect(3.f, 0.f, dockTile.size.width - 6.f, 20.f)];
208    
209    [fProgressIndicator setStyle:NSProgressIndicatorBarStyle];
210    [fProgressIndicator setIndeterminate:NO];
211    [[dockTile contentView] addSubview:fProgressIndicator];
212    [fProgressIndicator setMinValue:0];
213    [fProgressIndicator setMaxValue:100];
214    [fProgressIndicator setHidden:YES];
215    [fProgressIndicator release];
216
217    // If the java application has a bundle with an Info.plist file with
218    //  a CFBundleDocumentTypes entry, then it is set up to handle Open Doc
219    //  and Print Doc commands for these files. Therefore java AWT will
220    //  cache Open Doc and Print Doc events that are sent prior to a
221    //  listener being installed by the client java application.
222    NSBundle *bundle = [NSBundle mainBundle];
223    fHandlesDocumentTypes = [bundle objectForInfoDictionaryKey:@"CFBundleDocumentTypes"] != nil || [bundle _hasEAWTOverride:@"DocumentHandler"];
224    fHandlesURLTypes = [bundle objectForInfoDictionaryKey:@"CFBundleURLTypes"] != nil || [bundle _hasEAWTOverride:@"URLHandler"];
225    if (fHandlesURLTypes) {
226        [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self
227                                                           andSelector:@selector(_handleOpenURLEvent:withReplyEvent:)
228                                                         forEventClass:kInternetEventClass
229                                                            andEventID:kAEGetURL];
230    }
231
232    // By HIG, Preferences are not available unless there is a handler. By default in Mac OS X,
233    //  there is not a handler, but it is in the nib file for convenience.
234    removeMenuItem(self.fPreferencesMenu);
235
236    [self _updateAboutMenu:YES enabled:YES];
237
238    // Now that the AppKit objects are known and set up, initialize the model data
239    BOOL aboutAvailable = ([self.fAboutMenu menu] != nil);
240    BOOL aboutEnabled = (aboutAvailable && [self.fAboutMenu isEnabled] && ([self.fAboutMenu target] != nil));
241
242    BOOL prefsAvailable = ([self.fPreferencesMenu menu] != nil);
243    BOOL prefsEnabled = (prefsAvailable && [self.fPreferencesMenu isEnabled] && ([self.fPreferencesMenu target] != nil));
244
245    JNIEnv *env = [ThreadUtilities getJNIEnv];
246    static JNF_CLASS_CACHE(sjc_AppMenuBarHandler, "com/apple/eawt/_AppMenuBarHandler");
247    static JNF_STATIC_MEMBER_CACHE(sjm_initMenuStates, sjc_AppMenuBarHandler, "initMenuStates", "(ZZZZ)V");
248    JNFCallStaticVoidMethod(env, sjm_initMenuStates, aboutAvailable, aboutEnabled, prefsAvailable, prefsEnabled);
249
250    // register for the finish launching and system power off notifications by default
251    NSNotificationCenter *ctr = [NSNotificationCenter defaultCenter];
252    Class clz = [ApplicationDelegate class];
253    [ctr addObserver:clz selector:@selector(_willFinishLaunching) name:NSApplicationWillFinishLaunchingNotification object:nil];
254    [ctr addObserver:clz selector:@selector(_systemWillPowerOff) name:NSWorkspaceWillPowerOffNotification object:nil];
255    [ctr addObserver:clz selector:@selector(_appDidActivate) name:NSApplicationDidBecomeActiveNotification object:nil];
256    [ctr addObserver:clz selector:@selector(_appDidDeactivate) name:NSApplicationDidResignActiveNotification object:nil];
257    [ctr addObserver:clz selector:@selector(_appDidHide) name:NSApplicationDidHideNotification object:nil];
258    [ctr addObserver:clz selector:@selector(_appDidUnhide) name:NSApplicationDidUnhideNotification object:nil];
259
260    return self;
261}
262
263- (void)dealloc {
264    self.fPreferencesMenu = nil;
265    self.fAboutMenu = nil;
266    self.fDockMenu = nil;
267    self.fDefaultMenuBar = nil;
268    self.fProgressIndicator = nil;
269
270    [super dealloc];
271}
272
273#pragma mark Callbacks from AppKit
274
275static JNF_CLASS_CACHE(sjc_AppEventHandler, "com/apple/eawt/_AppEventHandler");
276
277- (void)_handleOpenURLEvent:(NSAppleEventDescriptor *)openURLEvent withReplyEvent:(NSAppleEventDescriptor *)replyEvent {
278AWT_ASSERT_APPKIT_THREAD;
279    if (!fHandlesURLTypes) return;
280
281    NSString *url = [[openURLEvent paramDescriptorForKeyword:keyDirectObject] stringValue];
282
283    //fprintf(stderr,"jm_handleOpenURL\n");
284    JNIEnv *env = [ThreadUtilities getJNIEnv];
285    jstring jURL = JNFNSToJavaString(env, url);
286    static JNF_STATIC_MEMBER_CACHE(jm_handleOpenURI, sjc_AppEventHandler, "handleOpenURI", "(Ljava/lang/String;)V");
287    JNFCallStaticVoidMethod(env, jm_handleOpenURI, jURL); // AWT_THREADING Safe (event)
288    (*env)->DeleteLocalRef(env, jURL);
289
290    [replyEvent insertDescriptor:[NSAppleEventDescriptor nullDescriptor] atIndex:0];
291}
292
293// Helper for both open file and print file methods
294// Creates a Java list of files from a native list of files
295- (jobject)_createFilePathArrayFrom:(NSArray *)filenames withEnv:(JNIEnv *)env {
296    static JNF_CLASS_CACHE(sjc_ArrayList, "java/util/ArrayList");
297    static JNF_CTOR_CACHE(jm_ArrayList_ctor, sjc_ArrayList, "(I)V");
298    static JNF_MEMBER_CACHE(jm_ArrayList_add, sjc_ArrayList, "add", "(Ljava/lang/Object;)Z");
299
300    jobject jFileNamesArray = JNFNewObject(env, jm_ArrayList_ctor, (jint)[filenames count]); // AWT_THREADING Safe (known object)
301    for (NSString *filename in filenames) {
302        jstring jFileName = JNFNormalizedJavaStringForPath(env, filename);
303        JNFCallVoidMethod(env, jFileNamesArray, jm_ArrayList_add, jFileName);
304    }
305
306    return jFileNamesArray;
307}
308
309// Open file handler
310- (void)application:(NSApplication *)theApplication openFiles:(NSArray *)fileNames {
311AWT_ASSERT_APPKIT_THREAD;
312    if (!fHandlesDocumentTypes) {
313        [theApplication replyToOpenOrPrint:NSApplicationDelegateReplyCancel];
314        return;
315    }
316
317    //fprintf(stderr,"jm_handleOpenFile\n");
318    JNIEnv *env = [ThreadUtilities getJNIEnv];
319
320    // if these files were opened from a Spotlight query, try to get the search text from the current AppleEvent
321    NSAppleEventDescriptor *currentEvent = [[NSAppleEventManager sharedAppleEventManager] currentAppleEvent];
322    NSString *searchString = [[currentEvent paramDescriptorForKeyword:keyAESearchText] stringValue];
323    jstring jSearchString = JNFNSToJavaString(env, searchString);
324
325    // convert the file names array
326    jobject jFileNamesArray = [self _createFilePathArrayFrom:fileNames withEnv:env];
327
328    static JNF_STATIC_MEMBER_CACHE(jm_handleOpenFiles, sjc_AppEventHandler, "handleOpenFiles", "(Ljava/util/List;Ljava/lang/String;)V");
329    JNFCallStaticVoidMethod(env, jm_handleOpenFiles, jFileNamesArray, jSearchString);
330    (*env)->DeleteLocalRef(env, jFileNamesArray);
331    (*env)->DeleteLocalRef(env, jSearchString);
332
333    [theApplication replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
334}
335
336// Print handler
337- (NSApplicationPrintReply)application:(NSApplication *)application printFiles:(NSArray *)fileNames withSettings:(NSDictionary *)printSettings showPrintPanels:(BOOL)showPrintPanels {
338AWT_ASSERT_APPKIT_THREAD;
339    if (!fHandlesDocumentTypes) return NSPrintingCancelled;
340
341    //fprintf(stderr,"jm_handlePrintFile\n");
342    JNIEnv *env = [ThreadUtilities getJNIEnv];
343    jobject jFileNamesArray = [self _createFilePathArrayFrom:fileNames withEnv:env];
344    static JNF_STATIC_MEMBER_CACHE(jm_handlePrintFile, sjc_AppEventHandler, "handlePrintFiles", "(Ljava/util/List;)V");
345    JNFCallStaticVoidMethod(env, jm_handlePrintFile, jFileNamesArray); // AWT_THREADING Safe (event)
346    (*env)->DeleteLocalRef(env, jFileNamesArray);
347
348    return NSPrintingSuccess;
349}
350
351// Open app handler, registered in -init
352+ (void)_notifyJava:(jint)notificationType {
353AWT_ASSERT_APPKIT_THREAD;
354
355    //fprintf(stderr,"jm_handleOpenApplication\n");
356    JNIEnv *env = [ThreadUtilities getJNIEnv];
357    static JNF_STATIC_MEMBER_CACHE(jm_handleNativeNotification, sjc_AppEventHandler, "handleNativeNotification", "(I)V");
358    JNFCallStaticVoidMethod(env, jm_handleNativeNotification, notificationType); // AWT_THREADING Safe (event)
359}
360
361// About menu handler
362- (void)_aboutMenuHandler {
363    [ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_ABOUT];
364}
365
366// Preferences handler
367- (void)_preferencesMenuHandler {
368    [ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_PREFS];
369}
370
371// Open app handler, registered in -init
372+ (void)_willFinishLaunching {
373    [self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_OPEN_APP];
374}
375
376// ReOpen app handler
377- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag {
378    [ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_REOPEN_APP];
379    return YES;
380}
381
382// Quit handler
383- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app {
384    [ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_QUIT];
385    return NSTerminateLater;
386}
387
388+ (void)_systemWillPowerOff {
389    [self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_SHUTDOWN];
390}
391
392+ (void)_appDidActivate {
393    [self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_ACTIVE_APP_GAINED];
394}
395
396+ (void)_appDidDeactivate {
397    [self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_ACTIVE_APP_LOST];
398}
399
400+ (void)_appDidHide {
401    [self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_APP_HIDDEN];
402}
403
404+ (void)_appDidUnhide {
405    [self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_APP_SHOWN];
406}
407
408+ (void)_sessionDidActivate {
409    [self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_USER_SESSION_ACTIVE];
410}
411
412+ (void)_sessionDidDeactivate {
413    [self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_USER_SESSION_INACTIVE];
414}
415
416+ (void)_screenDidSleep {
417    [self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_SCREEN_SLEEP];
418}
419
420+ (void)_screenDidWake {
421    [self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_SCREEN_WAKE];
422}
423
424+ (void)_systemDidSleep {
425    [self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_SYSTEM_SLEEP];
426}
427
428+ (void)_systemDidWake {
429    [self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_SYSTEM_WAKE];
430}
431
432+ (void)_registerForNotification:(NSNumber *)notificationTypeNum {
433    NSNotificationCenter *ctr = [[NSWorkspace sharedWorkspace] notificationCenter];
434    Class clz = [ApplicationDelegate class];
435
436    jint notificationType = [notificationTypeNum intValue];
437    switch (notificationType) {
438        case com_apple_eawt__AppEventHandler_REGISTER_USER_SESSION:
439            [ctr addObserver:clz selector:@selector(_sessionDidActivate) name:NSWorkspaceSessionDidBecomeActiveNotification object:nil];
440            [ctr addObserver:clz selector:@selector(_sessionDidDeactivate) name:NSWorkspaceSessionDidResignActiveNotification object:nil];
441            break;
442        case com_apple_eawt__AppEventHandler_REGISTER_SCREEN_SLEEP:
443            [ctr addObserver:clz selector:@selector(_screenDidSleep) name:NSWorkspaceScreensDidSleepNotification object:nil];
444            [ctr addObserver:clz selector:@selector(_screenDidWake) name:NSWorkspaceScreensDidWakeNotification object:nil];
445            break;
446        case com_apple_eawt__AppEventHandler_REGISTER_SYSTEM_SLEEP:
447            [ctr addObserver:clz selector:@selector(_systemDidSleep) name:NSWorkspaceWillSleepNotification object:nil];
448            [ctr addObserver:clz selector:@selector(_systemDidWake) name:NSWorkspaceDidWakeNotification object:nil];
449            break;
450        default:
451            NSLog(@"EAWT attempting to register for unknown notification: %d", (int)notificationType);
452            break;
453    }
454}
455
456// Retrieves the menu to be attached to the Dock icon (AppKit callback)
457- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
458AWT_ASSERT_APPKIT_THREAD;
459    return self.fDockMenu;
460}
461
462- (CMenuBar *)defaultMenuBar {
463    return [[self.fDefaultMenuBar retain] autorelease];
464}
465
466
467#pragma mark Helpers called on the main thread from Java
468
469// Sets a new NSImageView on the Dock tile
470+ (void)_setDockIconImage:(NSImage *)image {
471AWT_ASSERT_APPKIT_THREAD;
472
473    NSDockTile *dockTile = [NSApp dockTile];
474    if (image == nil) {
475        [dockTile setContentView:nil];
476        return;
477    }
478
479    // setup an image view for the dock tile
480    NSRect frame = NSMakeRect(0, 0, dockTile.size.width, dockTile.size.height);
481    NSImageView *dockImageView = [[NSImageView alloc] initWithFrame: frame];
482    [dockImageView setImageScaling:NSImageScaleProportionallyUpOrDown];
483    [dockImageView setImage:image];
484
485    [[ApplicationDelegate sharedDelegate].fProgressIndicator removeFromSuperview];
486    [dockImageView addSubview:[ApplicationDelegate sharedDelegate].fProgressIndicator];
487
488    // add it to the NSDockTile
489    [dockTile setContentView: dockImageView];
490    [dockTile display];
491
492    [dockImageView release];
493}
494
495+ (void)_setDockIconProgress:(NSNumber *)value {
496AWT_ASSERT_APPKIT_THREAD;
497
498    ApplicationDelegate *delegate = [ApplicationDelegate sharedDelegate];
499    if ([value doubleValue] >= 0 && [value doubleValue] <=100) {
500        [delegate.fProgressIndicator setDoubleValue:[value doubleValue]];
501        [delegate.fProgressIndicator setHidden:NO];
502    } else {
503        [delegate.fProgressIndicator setHidden:YES];
504    }
505
506    [[NSApp dockTile] display];
507}
508
509// Obtains the image of the Dock icon, either manually set, a drawn copy, or the default NSApplicationIcon
510+ (NSImage *)_dockIconImage {
511AWT_ASSERT_APPKIT_THREAD;
512
513    NSDockTile *dockTile = [NSApp dockTile];
514    NSView *view = [dockTile contentView];
515
516    if ([view isKindOfClass:[NSImageView class]]) {
517        NSImage *img = [((NSImageView *)view) image];
518        if (img) return img;
519    }
520
521    if (view == nil) {
522        return [NSImage imageNamed:@"NSApplicationIcon"];
523    }
524
525    NSRect frame = [view frame];
526    NSImage *image = [[NSImage alloc] initWithSize:frame.size];
527    [image lockFocus];
528    [view drawRect:frame];
529    [image unlockFocus];
530    [image autorelease];
531    return image;
532}
533
534@end
535
536
537#pragma mark Native JNI calls
538
539/*
540 * Class:     com_apple_eawt_Application
541 * Method:    nativeInitializeApplicationDelegate
542 * Signature: ()V
543 */
544JNIEXPORT void JNICALL Java_com_apple_eawt_Application_nativeInitializeApplicationDelegate
545(JNIEnv *env, jclass clz)
546{
547JNF_COCOA_ENTER(env);
548    // Force initialization to happen on AppKit thread!
549    [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
550        [ApplicationDelegate sharedDelegate];
551    }];
552JNF_COCOA_EXIT(env);
553}
554
555/*
556 * Class:     com_apple_eawt__AppEventHandler
557 * Method:    nativeOpenCocoaAboutWindow
558 * Signature: ()V
559 */
560JNIEXPORT void JNICALL Java_com_apple_eawt__1AppEventHandler_nativeOpenCocoaAboutWindow
561(JNIEnv *env, jclass clz)
562{
563JNF_COCOA_ENTER(env);
564
565    [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
566        [NSApp orderFrontStandardAboutPanel:nil];
567    }];
568
569JNF_COCOA_EXIT(env);
570}
571
572/*
573 * Class:     com_apple_eawt__AppEventHandler
574 * Method:    nativeReplyToAppShouldTerminate
575 * Signature: (Z)V
576 */
577JNIEXPORT void JNICALL Java_com_apple_eawt__1AppEventHandler_nativeReplyToAppShouldTerminate
578(JNIEnv *env, jclass clz, jboolean doTerminate)
579{
580JNF_COCOA_ENTER(env);
581
582    [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
583        [NSApp replyToApplicationShouldTerminate:doTerminate];
584    }];
585
586JNF_COCOA_EXIT(env);
587}
588
589/*
590 * Class:     com_apple_eawt__AppEventHandler
591 * Method:    nativeRegisterForNotification
592 * Signature: (I)V
593 */
594JNIEXPORT void JNICALL Java_com_apple_eawt__1AppEventHandler_nativeRegisterForNotification
595(JNIEnv *env, jclass clz, jint notificationType)
596{
597JNF_COCOA_ENTER(env);
598    [ThreadUtilities performOnMainThread:@selector(_registerForNotification:)
599                                      on:[ApplicationDelegate class]
600                              withObject:[NSNumber numberWithInt:notificationType]
601                           waitUntilDone:NO]; // AWT_THREADING Safe (non-blocking)
602JNF_COCOA_EXIT(env);
603}
604
605/*
606 * Class:     com_apple_eawt__AppDockIconHandler
607 * Method:    nativeSetDockMenu
608 * Signature: (J)V
609 */
610JNIEXPORT void JNICALL Java_com_apple_eawt__1AppDockIconHandler_nativeSetDockMenu
611(JNIEnv *env, jclass clz, jlong nsMenuPtr)
612{
613JNF_COCOA_ENTER(env);
614
615    NSMenu *menu = (NSMenu *)jlong_to_ptr(nsMenuPtr);
616    [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
617        [ApplicationDelegate sharedDelegate].fDockMenu = menu;
618    }];
619
620JNF_COCOA_EXIT(env);
621}
622
623/*
624 * Class:     com_apple_eawt__AppDockIconHandler
625 * Method:    nativeSetDockIconImage
626 * Signature: (J)V
627 */
628JNIEXPORT void JNICALL Java_com_apple_eawt__1AppDockIconHandler_nativeSetDockIconImage
629(JNIEnv *env, jclass clz, jlong nsImagePtr)
630{
631JNF_COCOA_ENTER(env);
632
633    NSImage *_image = (NSImage *)jlong_to_ptr(nsImagePtr);
634    [ThreadUtilities performOnMainThread:@selector(_setDockIconImage:)
635                                      on:[ApplicationDelegate class]
636                              withObject:_image
637                           waitUntilDone:NO];
638
639JNF_COCOA_EXIT(env);
640}
641
642/*
643 * Class:     com_apple_eawt__AppDockIconHandler
644 * Method:    nativeSetDockIconProgress
645 * Signature: (I)V
646 */
647JNIEXPORT void JNICALL Java_com_apple_eawt__1AppDockIconHandler_nativeSetDockIconProgress
648  (JNIEnv *env, jclass clz, jint value)
649{
650    JNF_COCOA_ENTER(env);
651
652     [ThreadUtilities performOnMainThread:@selector(_setDockIconProgress:)
653                                       on:[ApplicationDelegate class]
654                               withObject:[NSNumber numberWithInt:value]
655                            waitUntilDone:NO];
656
657    JNF_COCOA_EXIT(env);
658}
659
660/*
661 * Class:     com_apple_eawt__AppDockIconHandler
662 * Method:    nativeGetDockIconImage
663 * Signature: ()J
664 */
665JNIEXPORT jlong JNICALL Java_com_apple_eawt__1AppDockIconHandler_nativeGetDockIconImage
666(JNIEnv *env, jclass clz)
667{
668    __block NSImage *image = nil;
669
670JNF_COCOA_ENTER(env);
671
672    [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
673        image = [[ApplicationDelegate _dockIconImage] retain];
674    }];
675
676JNF_COCOA_EXIT(env);
677
678    return ptr_to_jlong(image);
679}
680
681/*
682 * Class:     com_apple_eawt__AppDockIconHandler
683 * Method:    nativeSetDockIconBadge
684 * Signature: (Ljava/lang/String;)V
685 */
686JNIEXPORT void JNICALL Java_com_apple_eawt__1AppDockIconHandler_nativeSetDockIconBadge
687(JNIEnv *env, jclass clz, jstring badge)
688{
689JNF_COCOA_ENTER(env);
690
691    NSString *badgeString = JNFJavaToNSString(env, badge);
692    [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
693        NSDockTile *dockTile = [NSApp dockTile];
694        [dockTile setBadgeLabel:badgeString];
695        [dockTile display];
696    }];
697
698JNF_COCOA_EXIT(env);
699}
700
701/*
702 * Class:     com_apple_eawt__AppMiscHandlers
703 * Method:    nativeRequestActivation
704 * Signature: (Z)V
705 */
706JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMiscHandlers_nativeRequestActivation
707(JNIEnv *env, jclass clz, jboolean allWindows)
708{
709JNF_COCOA_ENTER(env);
710
711    [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
712        NSApplicationActivationOptions options = allWindows ? NSApplicationActivateAllWindows : 0;
713        options |= NSApplicationActivateIgnoringOtherApps; // without this, nothing happens!
714        [[NSRunningApplication currentApplication] activateWithOptions:options];
715    }];
716
717JNF_COCOA_EXIT(env);
718}
719
720/*
721 * Class:     com_apple_eawt__AppMiscHandlers
722 * Method:    nativeRequestUserAttention
723 * Signature: (Z)V
724 */
725JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMiscHandlers_nativeRequestUserAttention
726(JNIEnv *env, jclass clz, jboolean critical)
727{
728JNF_COCOA_ENTER(env);
729
730    [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
731        [NSApp requestUserAttention:critical ? NSCriticalRequest : NSInformationalRequest];
732    }];
733
734JNF_COCOA_EXIT(env);
735}
736
737/*
738 * Class:     com_apple_eawt__AppMiscHandlers
739 * Method:    nativeOpenHelpViewer
740 * Signature: ()V
741 */
742JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMiscHandlers_nativeOpenHelpViewer
743(JNIEnv *env, jclass clz)
744{
745JNF_COCOA_ENTER(env);
746
747    [ThreadUtilities performOnMainThread:@selector(showHelp:)
748                                      on:NSApp
749                              withObject:nil
750                           waitUntilDone:NO];
751
752JNF_COCOA_EXIT(env);
753}
754
755/*
756 * Class:     com_apple_eawt__AppMiscHandlers
757 * Method:    nativeEnableSuddenTermination
758 * Signature: ()V
759 */
760JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMiscHandlers_nativeEnableSuddenTermination
761(JNIEnv *env, jclass clz)
762{
763JNF_COCOA_ENTER(env);
764
765    [[NSProcessInfo processInfo] enableSuddenTermination]; // Foundation thread-safe
766
767JNF_COCOA_EXIT(env);
768}
769
770/*
771 * Class:     com_apple_eawt__AppMiscHandlers
772 * Method:    nativeDisableSuddenTermination
773 * Signature: ()V
774 */
775JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMiscHandlers_nativeDisableSuddenTermination
776(JNIEnv *env, jclass clz)
777{
778JNF_COCOA_ENTER(env);
779
780    [[NSProcessInfo processInfo] disableSuddenTermination]; // Foundation thread-safe
781
782JNF_COCOA_EXIT(env);
783}
784
785/*
786 * Class:     com_apple_eawt__AppMenuBarHandler
787 * Method:    nativeSetMenuState
788 * Signature: (IZZ)V
789 */
790JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMenuBarHandler_nativeSetMenuState
791(JNIEnv *env, jclass clz, jint menuID, jboolean visible, jboolean enabled)
792{
793JNF_COCOA_ENTER(env);
794
795    [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
796        ApplicationDelegate *delegate = [ApplicationDelegate sharedDelegate];
797        switch (menuID) {
798            case com_apple_eawt__AppMenuBarHandler_MENU_ABOUT:
799                [delegate _updateAboutMenu:visible enabled:enabled];
800                break;
801            case com_apple_eawt__AppMenuBarHandler_MENU_PREFS:
802                [delegate _updatePreferencesMenu:visible enabled:enabled];
803                break;
804        }
805    }];
806
807JNF_COCOA_EXIT(env);
808}
809
810/*
811 * Class:     com_apple_eawt__AppMenuBarHandler
812 * Method:    nativeSetDefaultMenuBar
813 * Signature: (J)V
814 */
815JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMenuBarHandler_nativeSetDefaultMenuBar
816(JNIEnv *env, jclass clz, jlong cMenuBarPtr)
817{
818JNF_COCOA_ENTER(env);
819
820    CMenuBar *menu = (CMenuBar *)jlong_to_ptr(cMenuBarPtr);
821    [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
822        [ApplicationDelegate sharedDelegate].fDefaultMenuBar = menu;
823    }];
824
825JNF_COCOA_EXIT(env);
826}
827