1 /*
   2  * Copyright (c) 2011, 2014, 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 
  47 static void addMenuItem(NSMenuItem* menuItem, NSInteger index) {
  48 AWT_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 
  57 static void removeMenuItem(NSMenuItem* menuItem) {
  58 AWT_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 
 116 AWT_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 {
 132 AWT_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 {
 161 AWT_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 {
 187 AWT_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 
 275 static JNF_CLASS_CACHE(sjc_AppEventHandler, "com/apple/eawt/_AppEventHandler");
 276 
 277 - (void)_handleOpenURLEvent:(NSAppleEventDescriptor *)openURLEvent withReplyEvent:(NSAppleEventDescriptor *)replyEvent {
 278 AWT_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 {
 311 AWT_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 {
 338 AWT_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 {
 353 AWT_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 {
 458 AWT_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 {
 471 AWT_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 {
 496 AWT_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 {
 511 AWT_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  */
 544 JNIEXPORT void JNICALL Java_com_apple_eawt_Application_nativeInitializeApplicationDelegate
 545 (JNIEnv *env, jclass clz)
 546 {
 547 JNF_COCOA_ENTER(env);
 548     // Force initialization to happen on AppKit thread!
 549     [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
 550         [ApplicationDelegate sharedDelegate];
 551     }];
 552 JNF_COCOA_EXIT(env);
 553 }
 554 
 555 /*
 556  * Class:     com_apple_eawt__AppEventHandler
 557  * Method:    nativeOpenCocoaAboutWindow
 558  * Signature: ()V
 559  */
 560 JNIEXPORT void JNICALL Java_com_apple_eawt__1AppEventHandler_nativeOpenCocoaAboutWindow
 561 (JNIEnv *env, jclass clz)
 562 {
 563 JNF_COCOA_ENTER(env);
 564 
 565     [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
 566         [NSApp orderFrontStandardAboutPanel:nil];
 567     }];
 568 
 569 JNF_COCOA_EXIT(env);
 570 }
 571 
 572 /*
 573  * Class:     com_apple_eawt__AppEventHandler
 574  * Method:    nativeReplyToAppShouldTerminate
 575  * Signature: (Z)V
 576  */
 577 JNIEXPORT void JNICALL Java_com_apple_eawt__1AppEventHandler_nativeReplyToAppShouldTerminate
 578 (JNIEnv *env, jclass clz, jboolean doTerminate)
 579 {
 580 JNF_COCOA_ENTER(env);
 581 
 582     [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
 583         [NSApp replyToApplicationShouldTerminate:doTerminate];
 584     }];
 585 
 586 JNF_COCOA_EXIT(env);
 587 }
 588 
 589 /*
 590  * Class:     com_apple_eawt__AppEventHandler
 591  * Method:    nativeRegisterForNotification
 592  * Signature: (I)V
 593  */
 594 JNIEXPORT void JNICALL Java_com_apple_eawt__1AppEventHandler_nativeRegisterForNotification
 595 (JNIEnv *env, jclass clz, jint notificationType)
 596 {
 597 JNF_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)
 602 JNF_COCOA_EXIT(env);
 603 }
 604 
 605 /*
 606  * Class:     com_apple_eawt__AppDockIconHandler
 607  * Method:    nativeSetDockMenu
 608  * Signature: (J)V
 609  */
 610 JNIEXPORT void JNICALL Java_com_apple_eawt__1AppDockIconHandler_nativeSetDockMenu
 611 (JNIEnv *env, jclass clz, jlong nsMenuPtr)
 612 {
 613 JNF_COCOA_ENTER(env);
 614 
 615     NSMenu *menu = (NSMenu *)jlong_to_ptr(nsMenuPtr);
 616     [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
 617         [ApplicationDelegate sharedDelegate].fDockMenu = menu;
 618     }];
 619 
 620 JNF_COCOA_EXIT(env);
 621 }
 622 
 623 /*
 624  * Class:     com_apple_eawt__AppDockIconHandler
 625  * Method:    nativeSetDockIconImage
 626  * Signature: (J)V
 627  */
 628 JNIEXPORT void JNICALL Java_com_apple_eawt__1AppDockIconHandler_nativeSetDockIconImage
 629 (JNIEnv *env, jclass clz, jlong nsImagePtr)
 630 {
 631 JNF_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 
 639 JNF_COCOA_EXIT(env);
 640 }
 641 
 642 /*
 643  * Class:     com_apple_eawt__AppDockIconHandler
 644  * Method:    nativeSetDockIconProgress
 645  * Signature: (I)V
 646  */
 647 JNIEXPORT 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  */
 665 JNIEXPORT jlong JNICALL Java_com_apple_eawt__1AppDockIconHandler_nativeGetDockIconImage
 666 (JNIEnv *env, jclass clz)
 667 {
 668     __block NSImage *image = nil;
 669 
 670 JNF_COCOA_ENTER(env);
 671 
 672     [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
 673         image = [[ApplicationDelegate _dockIconImage] retain];
 674     }];
 675 
 676 JNF_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  */
 686 JNIEXPORT void JNICALL Java_com_apple_eawt__1AppDockIconHandler_nativeSetDockIconBadge
 687 (JNIEnv *env, jclass clz, jstring badge)
 688 {
 689 JNF_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 
 698 JNF_COCOA_EXIT(env);
 699 }
 700 
 701 /*
 702  * Class:     com_apple_eawt__AppMiscHandlers
 703  * Method:    nativeRequestActivation
 704  * Signature: (Z)V
 705  */
 706 JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMiscHandlers_nativeRequestActivation
 707 (JNIEnv *env, jclass clz, jboolean allWindows)
 708 {
 709 JNF_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 
 717 JNF_COCOA_EXIT(env);
 718 }
 719 
 720 /*
 721  * Class:     com_apple_eawt__AppMiscHandlers
 722  * Method:    nativeRequestUserAttention
 723  * Signature: (Z)V
 724  */
 725 JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMiscHandlers_nativeRequestUserAttention
 726 (JNIEnv *env, jclass clz, jboolean critical)
 727 {
 728 JNF_COCOA_ENTER(env);
 729 
 730     [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
 731         [NSApp requestUserAttention:critical ? NSCriticalRequest : NSInformationalRequest];
 732     }];
 733 
 734 JNF_COCOA_EXIT(env);
 735 }
 736 
 737 /*
 738  * Class:     com_apple_eawt__AppMiscHandlers
 739  * Method:    nativeOpenHelpViewer
 740  * Signature: ()V
 741  */
 742 JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMiscHandlers_nativeOpenHelpViewer
 743 (JNIEnv *env, jclass clz)
 744 {
 745 JNF_COCOA_ENTER(env);
 746 
 747     [ThreadUtilities performOnMainThread:@selector(showHelp:)
 748                                       on:NSApp
 749                               withObject:nil
 750                            waitUntilDone:NO];
 751 
 752 JNF_COCOA_EXIT(env);
 753 }
 754 
 755 /*
 756  * Class:     com_apple_eawt__AppMiscHandlers
 757  * Method:    nativeEnableSuddenTermination
 758  * Signature: ()V
 759  */
 760 JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMiscHandlers_nativeEnableSuddenTermination
 761 (JNIEnv *env, jclass clz)
 762 {
 763 JNF_COCOA_ENTER(env);
 764 
 765     [[NSProcessInfo processInfo] enableSuddenTermination]; // Foundation thread-safe
 766 
 767 JNF_COCOA_EXIT(env);
 768 }
 769 
 770 /*
 771  * Class:     com_apple_eawt__AppMiscHandlers
 772  * Method:    nativeDisableSuddenTermination
 773  * Signature: ()V
 774  */
 775 JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMiscHandlers_nativeDisableSuddenTermination
 776 (JNIEnv *env, jclass clz)
 777 {
 778 JNF_COCOA_ENTER(env);
 779 
 780     [[NSProcessInfo processInfo] disableSuddenTermination]; // Foundation thread-safe
 781 
 782 JNF_COCOA_EXIT(env);
 783 }
 784 
 785 /*
 786  * Class:     com_apple_eawt__AppMenuBarHandler
 787  * Method:    nativeSetMenuState
 788  * Signature: (IZZ)V
 789  */
 790 JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMenuBarHandler_nativeSetMenuState
 791 (JNIEnv *env, jclass clz, jint menuID, jboolean visible, jboolean enabled)
 792 {
 793 JNF_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 
 807 JNF_COCOA_EXIT(env);
 808 }
 809 
 810 /*
 811  * Class:     com_apple_eawt__AppMenuBarHandler
 812  * Method:    nativeSetDefaultMenuBar
 813  * Signature: (J)V
 814  */
 815 JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMenuBarHandler_nativeSetDefaultMenuBar
 816 (JNIEnv *env, jclass clz, jlong cMenuBarPtr)
 817 {
 818 JNF_COCOA_ENTER(env);
 819 
 820     CMenuBar *menu = (CMenuBar *)jlong_to_ptr(cMenuBarPtr);
 821     [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
 822         [ApplicationDelegate sharedDelegate].fDefaultMenuBar = menu;
 823     }];
 824 
 825 JNF_COCOA_EXIT(env);
 826 }