blob: 3bf4f779fb5dbc25bab89418c20a6e0c04827ca6 [file] [log] [blame]
/*
* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#import <AppKit/AppKit.h>
#import <JavaNativeFoundation/JavaNativeFoundation.h>
#import <JavaRuntimeSupport/JavaRuntimeSupport.h>
#import "CMenuBar.h"
#import "CMenu.h"
#import "ThreadUtilities.h"
#import "sun_lwawt_macosx_CMenuBar.h"
__attribute__((visibility("default")))
NSString *CMenuBarDidReuseItemNotification =
@"CMenuBarDidReuseItemNotification";
static CMenuBar *sActiveMenuBar = nil;
static NSMenu *sDefaultHelpMenu = nil;
static BOOL sSetupHelpMenu = NO;
@interface CMenuBar (CMenuBar_Private)
+ (void) addDefaultHelpMenu;
@end
@implementation CMenuBar
+ (void)clearMenuBarExcludingAppleMenu_OnAppKitThread:(BOOL) excludingAppleMenu {
AWT_ASSERT_APPKIT_THREAD;
// Remove all Java menus from the main bar.
NSMenu *theMainMenu = [NSApp mainMenu];
NSUInteger i, menuCount = [theMainMenu numberOfItems];
for (i = menuCount; i > 1; i--) {
NSUInteger index = i-1;
NSMenuItem *currItem = [theMainMenu itemAtIndex:index];
NSMenu *currMenu = [currItem submenu];
if (excludingAppleMenu && ![currMenu isJavaMenu]) {
continue;
}
[currItem setSubmenu:nil];
[theMainMenu removeItemAtIndex:index];
}
[CMenuBar addDefaultHelpMenu];
}
+ (BOOL) isActiveMenuBar:(CMenuBar *)inMenuBar {
return (sActiveMenuBar == inMenuBar);
}
- (id) initWithPeer:(jobject)peer {
AWT_ASSERT_APPKIT_THREAD;
self = [super initWithPeer: peer];
if (self) {
fMenuList = [[NSMutableArray alloc] init];
}
return self;
}
-(void) dealloc {
[fMenuList release];
fMenuList = nil;
[fHelpMenu release];
fHelpMenu = nil;
[super dealloc];
}
+ (void) activate:(CMenuBar *)menubar modallyDisabled:(BOOL)modallyDisabled {
AWT_ASSERT_APPKIT_THREAD;
if (!menubar) {
[CMenuBar clearMenuBarExcludingAppleMenu_OnAppKitThread:YES];
return;
}
@synchronized([CMenuBar class]) {
sActiveMenuBar = menubar;
}
@synchronized(menubar) {
menubar->fModallyDisabled = modallyDisabled;
}
NSUInteger i = 0, newMenuListSize = [menubar->fMenuList count];
NSMenu *theMainMenu = [NSApp mainMenu];
NSUInteger menuIndex, menuCount = [theMainMenu numberOfItems];
NSUInteger cmenuIndex = 0, cmenuCount = newMenuListSize;
NSMutableArray *removedMenuArray = [NSMutableArray array];
for (menuIndex = 0; menuIndex < menuCount; menuIndex++) {
NSMenuItem *currItem = [theMainMenu itemAtIndex:menuIndex];
NSMenu *currMenu = [currItem submenu];
if ([currMenu isJavaMenu]) {
// Ready to replace, find next candidate
CMenu *newMenu = nil;
if (cmenuIndex < cmenuCount) {
newMenu = (CMenu *)[menubar->fMenuList objectAtIndex:cmenuIndex];
if (newMenu == menubar->fHelpMenu) {
cmenuIndex++;
if (cmenuIndex < cmenuCount) {
newMenu = (CMenu *)[menubar->fMenuList objectAtIndex:cmenuIndex];
}
}
}
if (newMenu) {
NSMenu *menuToAdd = [newMenu menu];
if ([theMainMenu indexOfItemWithSubmenu:menuToAdd] == -1) {
[[NSNotificationCenter defaultCenter] postNotificationName:CMenuBarDidReuseItemNotification object:theMainMenu];
[currItem setSubmenu:menuToAdd];
[currItem setTitle:[menuToAdd title]];
cmenuIndex++;
}
BOOL newEnabledState = [newMenu isEnabled] && !menubar->fModallyDisabled;
[currItem setEnabled:newEnabledState];
} else {
[removedMenuArray addObject:[NSNumber numberWithInteger:menuIndex]];
}
}
}
// Clean up extra items
NSUInteger removedIndex, removedCount = [removedMenuArray count];
for (removedIndex=removedCount; removedIndex > 0; removedIndex--) {
NSUInteger index = [[removedMenuArray objectAtIndex:(removedIndex-1)] integerValue];
NSMenuItem *currItem = [theMainMenu itemAtIndex:index];
[currItem setSubmenu:nil];
[theMainMenu removeItemAtIndex:index];
}
i = cmenuIndex;
// Add all of the menus in the menu list.
for (; i < newMenuListSize; i++) {
CMenu *newMenu = (CMenu *)[menubar->fMenuList objectAtIndex:i];
if (newMenu != menubar->fHelpMenu) {
NSArray *args = [NSArray arrayWithObjects:newMenu, [NSNumber numberWithInt:-1], nil];
[menubar nativeAddMenuAtIndex_OnAppKitThread:args];
}
}
// Add the help menu last.
if (menubar->fHelpMenu) {
NSArray *args = [NSArray arrayWithObjects:menubar->fHelpMenu, [NSNumber numberWithInt:-1], nil];
[menubar nativeAddMenuAtIndex_OnAppKitThread:args];
} else {
[CMenuBar addDefaultHelpMenu];
}
}
-(void) deactivate {
AWT_ASSERT_APPKIT_THREAD;
@synchronized([CMenuBar class]) {
sActiveMenuBar = nil;
}
@synchronized(self) {
fModallyDisabled = NO;
}
}
-(void) javaAddMenu: (CMenu *)theMenu {
@synchronized(self) {
[fMenuList addObject: theMenu];
}
if (self == sActiveMenuBar) {
NSArray *args = [[NSArray alloc] initWithObjects:theMenu, [NSNumber numberWithInt:-1], nil];
[ThreadUtilities performOnMainThread:@selector(nativeAddMenuAtIndex_OnAppKitThread:) on:self withObject:args waitUntilDone:YES];
[args release];
}
}
// This method is a special case for use by the screen menu bar.
// See ScreenMenuBar.java -- used to implement setVisible(boolean) by
// removing or adding the menu from the current menu bar's list.
-(void) javaAddMenu: (CMenu *)theMenu atIndex:(jint)index {
@synchronized(self) {
if (index == -1){
[fMenuList addObject:theMenu];
}else{
[fMenuList insertObject:theMenu atIndex:index];
}
}
if (self == sActiveMenuBar) {
NSArray *args = [[NSArray alloc] initWithObjects:theMenu, [NSNumber numberWithInt:index], nil];
[ThreadUtilities performOnMainThread:@selector(nativeAddMenuAtIndex_OnAppKitThread:) on:self withObject:args waitUntilDone:YES];
[args release];
}
}
- (NSInteger) javaIndexToNSMenuIndex_OnAppKitThread:(jint)javaIndex {
AWT_ASSERT_APPKIT_THREAD;
NSInteger returnValue = -1;
NSMenu *theMainMenu = [NSApp mainMenu];
if (javaIndex == -1) {
if (fHelpMenu) {
returnValue = [theMainMenu indexOfItemWithSubmenu:[fHelpMenu menu]];
}
} else {
CMenu *requestedMenu = [fMenuList objectAtIndex:javaIndex];
if (requestedMenu == fHelpMenu) {
returnValue = [theMainMenu indexOfItemWithSubmenu:[fHelpMenu menu]];
} else {
NSUInteger i, menuCount = [theMainMenu numberOfItems];
jint currJavaMenuIndex = 0;
for (i = 0; i < menuCount; i++) {
NSMenuItem *currItem = [theMainMenu itemAtIndex:i];
NSMenu *currMenu = [currItem submenu];
if ([currMenu isJavaMenu]) {
if (javaIndex == currJavaMenuIndex) {
returnValue = i;
break;
}
currJavaMenuIndex++;
}
}
}
}
return returnValue;
}
- (void) nativeAddMenuAtIndex_OnAppKitThread:(NSArray *)args {
AWT_ASSERT_APPKIT_THREAD;
CMenu *theNewMenu = (CMenu*)[args objectAtIndex:0];
jint index = [(NSNumber*)[args objectAtIndex:1] intValue];
NSApplication *theApp = [NSApplication sharedApplication];
NSMenu *theMainMenu = [theApp mainMenu];
NSMenu *menuToAdd = [theNewMenu menu];
if ([theMainMenu indexOfItemWithSubmenu:menuToAdd] == -1) {
NSMenuItem *newItem = [[NSMenuItem alloc] init];
[newItem setSubmenu:[theNewMenu menu]];
[newItem setTitle:[[theNewMenu menu] title]];
NSInteger nsMenuIndex = [self javaIndexToNSMenuIndex_OnAppKitThread:index];
if (nsMenuIndex == -1) {
[theMainMenu addItem:newItem];
} else {
[theMainMenu insertItem:newItem atIndex:nsMenuIndex];
}
BOOL newEnabledState = [theNewMenu isEnabled] && !fModallyDisabled;
[newItem setEnabled:newEnabledState];
[newItem release];
}
}
- (void) javaDeleteMenu: (jint)index {
if (self == sActiveMenuBar) {
[ThreadUtilities performOnMainThread:@selector(nativeDeleteMenu_OnAppKitThread:) on:self withObject:[NSNumber numberWithInt:index] waitUntilDone:YES];
}
@synchronized(self) {
CMenu *menuToRemove = [fMenuList objectAtIndex:index];
if (menuToRemove == fHelpMenu) {
[fHelpMenu release];
fHelpMenu = nil;
}
[fMenuList removeObjectAtIndex:index];
}
}
- (void) nativeDeleteMenu_OnAppKitThread:(id)indexObj {
AWT_ASSERT_APPKIT_THREAD;
NSApplication *theApp = [NSApplication sharedApplication];
NSMenu *theMainMenu = [theApp mainMenu];
jint menuToRemove = [(NSNumber *)indexObj intValue];
NSInteger nsMenuToRemove = [self javaIndexToNSMenuIndex_OnAppKitThread:menuToRemove];
if (nsMenuToRemove != -1) {
[theMainMenu removeItemAtIndex:nsMenuToRemove];
}
}
- (void) javaSetHelpMenu:(CMenu *)theMenu {
@synchronized(self) {
[theMenu retain];
[fHelpMenu release];
fHelpMenu = theMenu;
}
}
+ (void) addDefaultHelpMenu {
AWT_ASSERT_APPKIT_THREAD;
// Look for a help book tag. If it's there, add the help menu.
@synchronized ([CMenuBar class]) {
if (!sSetupHelpMenu) {
if (sDefaultHelpMenu == nil) {
// If we are embedded, don't make a help menu.
// TODO(cpc): we don't have NSApplicationAWT yet...
//if (![NSApp isKindOfClass:[NSApplicationAWT class]]) {
// sSetupHelpMenu = YES;
// return;
//}
// If the developer specified a NIB, don't make a help menu.
// TODO(cpc): usingDefaultNib only defined on NSApplicationAWT
//if (![NSApp usingDefaultNib]) {
// sSetupHelpMenu = YES;
// return;
//}
// TODO: not implemented
}
sSetupHelpMenu = YES;
}
}
if (sDefaultHelpMenu) {
NSMenu *theMainMenu = [NSApp mainMenu];
if ([theMainMenu indexOfItemWithSubmenu:sDefaultHelpMenu] == -1) {
// Since we're re-using this NSMenu, we need to clear its parent before
// adding it to a new menu item, or else AppKit will complain.
[sDefaultHelpMenu setSupermenu:nil];
// Add the help menu to the main menu.
NSMenuItem *newItem = [[NSMenuItem alloc] init];
[newItem setSubmenu:sDefaultHelpMenu];
[newItem setTitle:[sDefaultHelpMenu title]];
[theMainMenu addItem:newItem];
// Release it so the main menu owns it.
[newItem release];
}
}
}
@end
/*
* Class: sun_lwawt_macosx_CMenuBar
* Method: nativeCreateMenuBar
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_sun_lwawt_macosx_CMenuBar_nativeCreateMenuBar
(JNIEnv *env, jobject peer)
{
CMenuBar *aCMenuBar = nil;
JNF_COCOA_ENTER(env);
jobject cPeerObjGlobal = (*env)->NewGlobalRef(env, peer);
// We use an array here only to be able to get a return value
NSMutableArray *args = [[NSMutableArray alloc] initWithObjects:[NSValue valueWithBytes:&cPeerObjGlobal objCType:@encode(jobject)], nil];
[ThreadUtilities performOnMainThread:@selector(_create_OnAppKitThread:) on:[CMenuBar alloc] withObject:args waitUntilDone:YES];
aCMenuBar = (CMenuBar *)[args objectAtIndex: 0];
if (aCMenuBar == nil) {
return 0L;
}
// [args release];
// A strange memory managment after that.
JNF_COCOA_EXIT(env);
if (aCMenuBar) {
CFRetain(aCMenuBar); // GC
[aCMenuBar release];
}
return ptr_to_jlong(aCMenuBar);
}
/*
* Class: sun_lwawt_macosx_CMenuBar
* Method: nativeAddAtIndex
* Signature: (JJI)V
*/
JNIEXPORT void JNICALL
Java_sun_lwawt_macosx_CMenuBar_nativeAddAtIndex
(JNIEnv *env, jobject peer,
jlong menuBarObject, jlong menuObject, jint index)
{
JNF_COCOA_ENTER(env);
// Remove the specified item.
[((CMenuBar *) jlong_to_ptr(menuBarObject)) javaAddMenu:(CMenu *) jlong_to_ptr(menuObject) atIndex:index];
JNF_COCOA_EXIT(env);
}
/*
* Class: sun_lwawt_macosx_CMenuBar
* Method: nativeDelMenu
* Signature: (JI)V
*/
JNIEXPORT void JNICALL
Java_sun_lwawt_macosx_CMenuBar_nativeDelMenu
(JNIEnv *env, jobject peer, jlong menuBarObject, jint index)
{
JNF_COCOA_ENTER(env);
// Remove the specified item.
[((CMenuBar *) jlong_to_ptr(menuBarObject)) javaDeleteMenu: index];
JNF_COCOA_EXIT(env);
}
/*
* Class: sun_lwawt_macosx_CMenuBar
* Method: nativeSetHelpMenu
* Signature: (JJ)V
*/
JNIEXPORT void JNICALL
Java_sun_lwawt_macosx_CMenuBar_nativeSetHelpMenu
(JNIEnv *env, jobject peer, jlong menuBarObject, jlong menuObject)
{
JNF_COCOA_ENTER(env);
// Remove the specified item.
[((CMenuBar *) jlong_to_ptr(menuBarObject)) javaSetHelpMenu: ((CMenu *)jlong_to_ptr(menuObject))];
JNF_COCOA_EXIT(env);
}