| /* |
| * 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 <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); |
| } |