Path: blob/master/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuBar.m
41152 views
/*1* Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#import "JNIUtilities.h"2627#import <AppKit/AppKit.h>28#import <JavaRuntimeSupport/JavaRuntimeSupport.h>293031#import "CMenuBar.h"32#import "CMenu.h"33#import "ThreadUtilities.h"34#import "ApplicationDelegate.h"3536#import "sun_lwawt_macosx_CMenuBar.h"3738__attribute__((visibility("default")))39NSString *CMenuBarDidReuseItemNotification =40@"CMenuBarDidReuseItemNotification";4142static CMenuBar *sActiveMenuBar = nil;43static NSMenu *sDefaultHelpMenu = nil;44static BOOL sSetupHelpMenu = NO;4546@interface CMenuBar (CMenuBar_Private)47+ (void) addDefaultHelpMenu;48@end4950@implementation CMenuBar5152+ (void)clearMenuBarExcludingAppleMenu_OnAppKitThread:(BOOL) excludingAppleMenu {53AWT_ASSERT_APPKIT_THREAD;54// Remove all Java menus from the main bar.55NSMenu *theMainMenu = [NSApp mainMenu];56NSUInteger i, menuCount = [theMainMenu numberOfItems];5758for (i = menuCount; i > 1; i--) {59NSUInteger index = i-1;6061NSMenuItem *currItem = [theMainMenu itemAtIndex:index];62NSMenu *currMenu = [currItem submenu];6364if (excludingAppleMenu && ![currMenu isJavaMenu]) {65continue;66}67[currItem setSubmenu:nil];68[theMainMenu removeItemAtIndex:index];69}7071[CMenuBar addDefaultHelpMenu];72}7374+ (BOOL) isActiveMenuBar:(CMenuBar *)inMenuBar {75return (sActiveMenuBar == inMenuBar);76}7778- (id) initWithPeer:(jobject)peer {79AWT_ASSERT_APPKIT_THREAD;80self = [super initWithPeer: peer];81if (self) {82fMenuList = [[NSMutableArray alloc] init];83}84return self;85}8687-(void) dealloc {88[fMenuList release];89fMenuList = nil;9091[fHelpMenu release];92fHelpMenu = nil;9394[super dealloc];95}9697+ (void) activate:(CMenuBar *)menubar modallyDisabled:(BOOL)modallyDisabled {98AWT_ASSERT_APPKIT_THREAD;99100if (!menubar) {101[CMenuBar clearMenuBarExcludingAppleMenu_OnAppKitThread:YES];102return;103}104105#ifdef DEBUG106NSLog(@"activating menu bar: %@", menubar);107#endif108109@synchronized([CMenuBar class]) {110sActiveMenuBar = menubar;111}112113@synchronized(menubar) {114menubar->fModallyDisabled = modallyDisabled;115}116117NSUInteger i = 0, newMenuListSize = [menubar->fMenuList count];118119NSMenu *theMainMenu = [NSApp mainMenu];120NSUInteger menuIndex, menuCount = [theMainMenu numberOfItems];121122NSUInteger cmenuIndex = 0, cmenuCount = newMenuListSize;123NSMutableArray *removedMenuArray = [NSMutableArray array];124125for (menuIndex = 0; menuIndex < menuCount; menuIndex++) {126NSMenuItem *currItem = [theMainMenu itemAtIndex:menuIndex];127NSMenu *currMenu = [currItem submenu];128129if ([currMenu isJavaMenu]) {130// Ready to replace, find next candidate131CMenu *newMenu = nil;132if (cmenuIndex < cmenuCount) {133newMenu = (CMenu *)[menubar->fMenuList objectAtIndex:cmenuIndex];134if (newMenu == menubar->fHelpMenu) {135cmenuIndex++;136if (cmenuIndex < cmenuCount) {137newMenu = (CMenu *)[menubar->fMenuList objectAtIndex:cmenuIndex];138}139}140}141if (newMenu) {142NSMenu *menuToAdd = [newMenu menu];143if ([theMainMenu indexOfItemWithSubmenu:menuToAdd] == -1) {144[[NSNotificationCenter defaultCenter] postNotificationName:CMenuBarDidReuseItemNotification object:theMainMenu];145146[currItem setSubmenu:menuToAdd];147[currItem setTitle:[menuToAdd title]];148cmenuIndex++;149}150151BOOL newEnabledState = [newMenu isEnabled] && !menubar->fModallyDisabled;152[currItem setEnabled:newEnabledState];153} else {154[removedMenuArray addObject:[NSNumber numberWithInteger:menuIndex]];155}156}157}158159// Clean up extra items160NSUInteger removedIndex, removedCount = [removedMenuArray count];161for (removedIndex=removedCount; removedIndex > 0; removedIndex--) {162NSUInteger index = [[removedMenuArray objectAtIndex:(removedIndex-1)] integerValue];163NSMenuItem *currItem = [theMainMenu itemAtIndex:index];164[currItem setSubmenu:nil];165[theMainMenu removeItemAtIndex:index];166}167168i = cmenuIndex;169170// Add all of the menus in the menu list.171for (; i < newMenuListSize; i++) {172CMenu *newMenu = (CMenu *)[menubar->fMenuList objectAtIndex:i];173174if (newMenu != menubar->fHelpMenu) {175NSArray *args = [NSArray arrayWithObjects:newMenu, [NSNumber numberWithInt:-1], nil];176[menubar nativeAddMenuAtIndex_OnAppKitThread:args];177}178}179180// Add the help menu last.181if (menubar->fHelpMenu) {182NSArray *args = [NSArray arrayWithObjects:menubar->fHelpMenu, [NSNumber numberWithInt:-1], nil];183[menubar nativeAddMenuAtIndex_OnAppKitThread:args];184} else {185[CMenuBar addDefaultHelpMenu];186}187}188189-(void) deactivate {190AWT_ASSERT_APPKIT_THREAD;191192BOOL isDeactivated = NO;193@synchronized([CMenuBar class]) {194if (sActiveMenuBar == self) {195sActiveMenuBar = nil;196isDeactivated = YES;197}198}199200if (isDeactivated) {201#ifdef DEBUG202NSLog(@"deactivating menu bar: %@", self);203#endif204205@synchronized(self) {206fModallyDisabled = NO;207}208209// In theory, this might cause flickering if the window gaining focus210// has its own menu. However, I couldn't reproduce it on practice, so211// perhaps this is a non issue.212CMenuBar* defaultMenu = [[ApplicationDelegate sharedDelegate] defaultMenuBar];213if (defaultMenu != nil) {214[CMenuBar activate:defaultMenu modallyDisabled:NO];215}216}217}218219-(void) javaAddMenu: (CMenu *)theMenu {220@synchronized(self) {221[fMenuList addObject: theMenu];222}223224if (self == sActiveMenuBar) {225NSArray *args = [[NSArray alloc] initWithObjects:theMenu, [NSNumber numberWithInt:-1], nil];226[ThreadUtilities performOnMainThread:@selector(nativeAddMenuAtIndex_OnAppKitThread:) on:self withObject:args waitUntilDone:YES];227[args release];228}229}230231// This method is a special case for use by the screen menu bar.232// See ScreenMenuBar.java -- used to implement setVisible(boolean) by233// removing or adding the menu from the current menu bar's list.234-(void) javaAddMenu: (CMenu *)theMenu atIndex:(jint)index {235@synchronized(self) {236if (index == -1){237[fMenuList addObject:theMenu];238}else{239[fMenuList insertObject:theMenu atIndex:index];240}241}242243if (self == sActiveMenuBar) {244NSArray *args = [[NSArray alloc] initWithObjects:theMenu, [NSNumber numberWithInt:index], nil];245[ThreadUtilities performOnMainThread:@selector(nativeAddMenuAtIndex_OnAppKitThread:) on:self withObject:args waitUntilDone:YES];246[args release];247}248}249250- (NSInteger) javaIndexToNSMenuIndex_OnAppKitThread:(jint)javaIndex {251AWT_ASSERT_APPKIT_THREAD;252NSInteger returnValue = -1;253NSMenu *theMainMenu = [NSApp mainMenu];254255if (javaIndex == -1) {256if (fHelpMenu) {257returnValue = [theMainMenu indexOfItemWithSubmenu:[fHelpMenu menu]];258}259} else {260CMenu *requestedMenu = [fMenuList objectAtIndex:javaIndex];261262if (requestedMenu == fHelpMenu) {263returnValue = [theMainMenu indexOfItemWithSubmenu:[fHelpMenu menu]];264} else {265NSUInteger i, menuCount = [theMainMenu numberOfItems];266jint currJavaMenuIndex = 0;267for (i = 0; i < menuCount; i++) {268NSMenuItem *currItem = [theMainMenu itemAtIndex:i];269NSMenu *currMenu = [currItem submenu];270271if ([currMenu isJavaMenu]) {272if (javaIndex == currJavaMenuIndex) {273returnValue = i;274break;275}276277currJavaMenuIndex++;278}279}280}281}282283return returnValue;284}285286- (void) nativeAddMenuAtIndex_OnAppKitThread:(NSArray *)args {287AWT_ASSERT_APPKIT_THREAD;288CMenu *theNewMenu = (CMenu*)[args objectAtIndex:0];289jint index = [(NSNumber*)[args objectAtIndex:1] intValue];290NSApplication *theApp = [NSApplication sharedApplication];291NSMenu *theMainMenu = [theApp mainMenu];292NSMenu *menuToAdd = [theNewMenu menu];293294if ([theMainMenu indexOfItemWithSubmenu:menuToAdd] == -1) {295NSMenuItem *newItem = [[NSMenuItem alloc] init];296[newItem setSubmenu:[theNewMenu menu]];297[newItem setTitle:[[theNewMenu menu] title]];298299NSInteger nsMenuIndex = [self javaIndexToNSMenuIndex_OnAppKitThread:index];300301if (nsMenuIndex == -1) {302[theMainMenu addItem:newItem];303} else {304[theMainMenu insertItem:newItem atIndex:nsMenuIndex];305}306307BOOL newEnabledState = [theNewMenu isEnabled] && !fModallyDisabled;308[newItem setEnabled:newEnabledState];309[newItem release];310}311}312313- (void) javaDeleteMenu: (jint)index {314if (self == sActiveMenuBar) {315[ThreadUtilities performOnMainThread:@selector(nativeDeleteMenu_OnAppKitThread:) on:self withObject:[NSNumber numberWithInt:index] waitUntilDone:YES];316}317318@synchronized(self) {319CMenu *menuToRemove = [fMenuList objectAtIndex:index];320321if (menuToRemove == fHelpMenu) {322[fHelpMenu release];323fHelpMenu = nil;324}325326[fMenuList removeObjectAtIndex:index];327}328}329330- (void) nativeDeleteMenu_OnAppKitThread:(id)indexObj {331AWT_ASSERT_APPKIT_THREAD;332NSApplication *theApp = [NSApplication sharedApplication];333NSMenu *theMainMenu = [theApp mainMenu];334jint menuToRemove = [(NSNumber *)indexObj intValue];335NSInteger nsMenuToRemove = [self javaIndexToNSMenuIndex_OnAppKitThread:menuToRemove];336337if (nsMenuToRemove != -1) {338[theMainMenu removeItemAtIndex:nsMenuToRemove];339}340}341342- (void) javaSetHelpMenu:(CMenu *)theMenu {343@synchronized(self) {344[theMenu retain];345[fHelpMenu release];346fHelpMenu = theMenu;347}348}349350+ (void) addDefaultHelpMenu {351AWT_ASSERT_APPKIT_THREAD;352353// Look for a help book tag. If it's there, add the help menu.354@synchronized ([CMenuBar class]) {355if (!sSetupHelpMenu) {356if (sDefaultHelpMenu == nil) {357// If we are embedded, don't make a help menu.358// TODO(cpc): we don't have NSApplicationAWT yet...359//if (![NSApp isKindOfClass:[NSApplicationAWT class]]) {360// sSetupHelpMenu = YES;361// return;362//}363364// If the developer specified a NIB, don't make a help menu.365// TODO(cpc): usingDefaultNib only defined on NSApplicationAWT366//if (![NSApp usingDefaultNib]) {367// sSetupHelpMenu = YES;368// return;369//}370371// TODO: not implemented372}373374sSetupHelpMenu = YES;375}376}377378if (sDefaultHelpMenu) {379NSMenu *theMainMenu = [NSApp mainMenu];380381if ([theMainMenu indexOfItemWithSubmenu:sDefaultHelpMenu] == -1) {382// Since we're re-using this NSMenu, we need to clear its parent before383// adding it to a new menu item, or else AppKit will complain.384[sDefaultHelpMenu setSupermenu:nil];385386// Add the help menu to the main menu.387NSMenuItem *newItem = [[NSMenuItem alloc] init];388[newItem setSubmenu:sDefaultHelpMenu];389[newItem setTitle:[sDefaultHelpMenu title]];390[theMainMenu addItem:newItem];391392// Release it so the main menu owns it.393[newItem release];394}395}396}397398@end399400/*401* Class: sun_lwawt_macosx_CMenuBar402* Method: nativeCreateMenuBar403* Signature: ()J404*/405JNIEXPORT jlong JNICALL406Java_sun_lwawt_macosx_CMenuBar_nativeCreateMenuBar407(JNIEnv *env, jobject peer)408{409__block CMenuBar *aCMenuBar = nil;410JNI_COCOA_ENTER(env);411412jobject cPeerObjGlobal = (*env)->NewGlobalRef(env, peer);413414[ThreadUtilities performOnMainThreadWaiting:YES block:^(){415416aCMenuBar = [[CMenuBar alloc] initWithPeer:cPeerObjGlobal];417// the aCMenuBar is released in CMenuComponent.dispose()418}];419if (aCMenuBar == nil) {420return 0L;421}422423JNI_COCOA_EXIT(env);424return ptr_to_jlong(aCMenuBar);425}426427/*428* Class: sun_lwawt_macosx_CMenuBar429* Method: nativeAddAtIndex430* Signature: (JJI)V431*/432JNIEXPORT void JNICALL433Java_sun_lwawt_macosx_CMenuBar_nativeAddAtIndex434(JNIEnv *env, jobject peer,435jlong menuBarObject, jlong menuObject, jint index)436{437JNI_COCOA_ENTER(env);438// Remove the specified item.439[((CMenuBar *) jlong_to_ptr(menuBarObject)) javaAddMenu:(CMenu *) jlong_to_ptr(menuObject) atIndex:index];440JNI_COCOA_EXIT(env);441}442443/*444* Class: sun_lwawt_macosx_CMenuBar445* Method: nativeDelMenu446* Signature: (JI)V447*/448JNIEXPORT void JNICALL449Java_sun_lwawt_macosx_CMenuBar_nativeDelMenu450(JNIEnv *env, jobject peer, jlong menuBarObject, jint index)451{452JNI_COCOA_ENTER(env);453// Remove the specified item.454[((CMenuBar *) jlong_to_ptr(menuBarObject)) javaDeleteMenu: index];455JNI_COCOA_EXIT(env);456}457458/*459* Class: sun_lwawt_macosx_CMenuBar460* Method: nativeSetHelpMenu461* Signature: (JJ)V462*/463JNIEXPORT void JNICALL464Java_sun_lwawt_macosx_CMenuBar_nativeSetHelpMenu465(JNIEnv *env, jobject peer, jlong menuBarObject, jlong menuObject)466{467JNI_COCOA_ENTER(env);468// Remove the specified item.469[((CMenuBar *) jlong_to_ptr(menuBarObject)) javaSetHelpMenu: ((CMenu *)jlong_to_ptr(menuObject))];470JNI_COCOA_EXIT(env);471}472473474