/* * Copyright (c) 2011, 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 #import #import #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); }