| /* | |
| * Copyright (c) 2011, 2024, 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 "JNIUtilities.h" | |
| #import <AppKit/AppKit.h> | |
| #import <JavaRuntimeSupport/JavaRuntimeSupport.h> | |
| #import "CMenuBar.h" | |
| #import "CMenu.h" | |
| #import "ThreadUtilities.h" | |
| #import "ApplicationDelegate.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; | |
| } | |
| #ifdef DEBUG | |
| NSLog(@"activating menu bar: %@", menubar); | |
| #endif | |
| @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; | |
| BOOL isDeactivated = NO; | |
| @synchronized([CMenuBar class]) { | |
| if (sActiveMenuBar == self) { | |
| sActiveMenuBar = nil; | |
| isDeactivated = YES; | |
| } | |
| } | |
| if (isDeactivated) { | |
| #ifdef DEBUG | |
| NSLog(@"deactivating menu bar: %@", self); | |
| #endif | |
| @synchronized(self) { | |
| fModallyDisabled = NO; | |
| } | |
| // In theory, this might cause flickering if the window gaining focus | |
| // has its own menu. However, I couldn't reproduce it on practice, so | |
| // perhaps this is a non issue. | |
| if ([ApplicationDelegate sharedDelegate] != nil) { | |
| CMenuBar* defaultMenu = [[ApplicationDelegate sharedDelegate] defaultMenuBar]; | |
| if (defaultMenu != nil) { | |
| [CMenuBar activate:defaultMenu modallyDisabled: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) | |
| { | |
| __block CMenuBar *aCMenuBar = nil; | |
| JNI_COCOA_ENTER(env); | |
| jobject cPeerObjGlobal = (*env)->NewGlobalRef(env, peer); | |
| [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ | |
| aCMenuBar = [[CMenuBar alloc] initWithPeer:cPeerObjGlobal]; | |
| // the aCMenuBar is released in CMenuComponent.dispose() | |
| }]; | |
| if (aCMenuBar == nil) { | |
| return 0L; | |
| } | |
| JNI_COCOA_EXIT(env); | |
| 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) | |
| { | |
| JNI_COCOA_ENTER(env); | |
| // Add the specified item. | |
| [((CMenuBar *) jlong_to_ptr(menuBarObject)) javaAddMenu:(CMenu *) jlong_to_ptr(menuObject) atIndex:index]; | |
| JNI_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) | |
| { | |
| JNI_COCOA_ENTER(env); | |
| // Remove the specified item. | |
| [((CMenuBar *) jlong_to_ptr(menuBarObject)) javaDeleteMenu: index]; | |
| JNI_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) | |
| { | |
| JNI_COCOA_ENTER(env); | |
| // Remove the specified item. | |
| [((CMenuBar *) jlong_to_ptr(menuBarObject)) javaSetHelpMenu: ((CMenu *)jlong_to_ptr(menuObject))]; | |
| JNI_COCOA_EXIT(env); | |
| } |