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