1 /* 2 * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #import <AppKit/AppKit.h> 27 #import <JavaNativeFoundation/JavaNativeFoundation.h> 28 #import <JavaRuntimeSupport/JavaRuntimeSupport.h> 29 30 31 #import "CMenuBar.h" 32 #import "CMenu.h" 33 #import "ThreadUtilities.h" 34 35 #import "sun_lwawt_macosx_CMenuBar.h" 36 37 __attribute__((visibility("default"))) 38 NSString *CMenuBarDidReuseItemNotification = 39 @"CMenuBarDidReuseItemNotification"; 40 41 static CMenuBar *sActiveMenuBar = nil; 42 static NSMenu *sDefaultHelpMenu = nil; 43 static BOOL sSetupHelpMenu = NO; 44 45 @interface CMenuBar (CMenuBar_Private) 46 + (void) addDefaultHelpMenu; 47 @end 48 49 @implementation CMenuBar 50 51 + (void)clearMenuBarExcludingAppleMenu_OnAppKitThread:(BOOL) excludingAppleMenu { 52 AWT_ASSERT_APPKIT_THREAD; 53 // Remove all Java menus from the main bar. 54 NSMenu *theMainMenu = [NSApp mainMenu]; 55 NSUInteger i, menuCount = [theMainMenu numberOfItems]; 56 57 for (i = menuCount; i > 1; i--) { 58 NSUInteger index = i-1; 59 60 NSMenuItem *currItem = [theMainMenu itemAtIndex:index]; 61 NSMenu *currMenu = [currItem submenu]; 62 63 if (excludingAppleMenu && ![currMenu isJavaMenu]) { 64 continue; 65 } 66 [currItem setSubmenu:nil]; 67 [theMainMenu removeItemAtIndex:index]; 68 } 69 70 [CMenuBar addDefaultHelpMenu]; 71 } 72 73 + (BOOL) isActiveMenuBar:(CMenuBar *)inMenuBar { 74 return (sActiveMenuBar == inMenuBar); 75 } 76 77 - (id) initWithPeer:(jobject)peer { 78 AWT_ASSERT_APPKIT_THREAD; 79 self = [super initWithPeer: peer]; 80 if (self) { 81 fMenuList = [[NSMutableArray alloc] init]; 82 } 83 return self; 84 } 85 86 -(void) dealloc { 87 [fMenuList release]; 88 fMenuList = nil; 89 90 [fHelpMenu release]; 91 fHelpMenu = nil; 92 93 [super dealloc]; 94 } 95 96 + (void) activate:(CMenuBar *)menubar modallyDisabled:(BOOL)modallyDisabled { 97 AWT_ASSERT_APPKIT_THREAD; 98 99 if (!menubar) { 100 [CMenuBar clearMenuBarExcludingAppleMenu_OnAppKitThread:YES]; 101 return; 102 } 103 104 @synchronized([CMenuBar class]) { 105 sActiveMenuBar = menubar; 106 } 107 108 @synchronized(menubar) { 109 menubar->fModallyDisabled = modallyDisabled; 110 } 111 112 NSUInteger i = 0, newMenuListSize = [menubar->fMenuList count]; 113 114 NSMenu *theMainMenu = [NSApp mainMenu]; 115 NSUInteger menuIndex, menuCount = [theMainMenu numberOfItems]; 116 117 NSUInteger cmenuIndex = 0, cmenuCount = newMenuListSize; 118 NSMutableArray *removedMenuArray = [NSMutableArray array]; 119 120 for (menuIndex = 0; menuIndex < menuCount; menuIndex++) { 121 NSMenuItem *currItem = [theMainMenu itemAtIndex:menuIndex]; 122 NSMenu *currMenu = [currItem submenu]; 123 124 if ([currMenu isJavaMenu]) { 125 // Ready to replace, find next candidate 126 CMenu *newMenu = nil; 127 if (cmenuIndex < cmenuCount) { 128 newMenu = (CMenu *)[menubar->fMenuList objectAtIndex:cmenuIndex]; 129 if (newMenu == menubar->fHelpMenu) { 130 cmenuIndex++; 131 if (cmenuIndex < cmenuCount) { 132 newMenu = (CMenu *)[menubar->fMenuList objectAtIndex:cmenuIndex]; 133 } 134 } 135 } 136 if (newMenu) { 137 NSMenu *menuToAdd = [newMenu menu]; 138 if ([theMainMenu indexOfItemWithSubmenu:menuToAdd] == -1) { 139 [[NSNotificationCenter defaultCenter] postNotificationName:CMenuBarDidReuseItemNotification object:theMainMenu]; 140 141 [currItem setSubmenu:menuToAdd]; 142 [currItem setTitle:[menuToAdd title]]; 143 cmenuIndex++; 144 } 145 146 BOOL newEnabledState = [newMenu isEnabled] && !menubar->fModallyDisabled; 147 [currItem setEnabled:newEnabledState]; 148 } else { 149 [removedMenuArray addObject:[NSNumber numberWithInteger:menuIndex]]; 150 } 151 } 152 } 153 154 // Clean up extra items 155 NSUInteger removedIndex, removedCount = [removedMenuArray count]; 156 for (removedIndex=removedCount; removedIndex > 0; removedIndex--) { 157 NSUInteger index = [[removedMenuArray objectAtIndex:(removedIndex-1)] integerValue]; 158 NSMenuItem *currItem = [theMainMenu itemAtIndex:index]; 159 [currItem setSubmenu:nil]; 160 [theMainMenu removeItemAtIndex:index]; 161 } 162 163 i = cmenuIndex; 164 165 // Add all of the menus in the menu list. 166 for (; i < newMenuListSize; i++) { 167 CMenu *newMenu = (CMenu *)[menubar->fMenuList objectAtIndex:i]; 168 169 if (newMenu != menubar->fHelpMenu) { 170 NSArray *args = [NSArray arrayWithObjects:newMenu, [NSNumber numberWithInt:-1], nil]; 171 [menubar nativeAddMenuAtIndex_OnAppKitThread:args]; 172 } 173 } 174 175 // Add the help menu last. 176 if (menubar->fHelpMenu) { 177 NSArray *args = [NSArray arrayWithObjects:menubar->fHelpMenu, [NSNumber numberWithInt:-1], nil]; 178 [menubar nativeAddMenuAtIndex_OnAppKitThread:args]; 179 } else { 180 [CMenuBar addDefaultHelpMenu]; 181 } 182 } 183 184 -(void) deactivate { 185 AWT_ASSERT_APPKIT_THREAD; 186 187 @synchronized([CMenuBar class]) { 188 sActiveMenuBar = nil; 189 } 190 191 @synchronized(self) { 192 fModallyDisabled = NO; 193 } 194 } 195 196 -(void) javaAddMenu: (CMenu *)theMenu { 197 @synchronized(self) { 198 [fMenuList addObject: theMenu]; 199 } 200 201 if (self == sActiveMenuBar) { 202 NSArray *args = [[NSArray alloc] initWithObjects:theMenu, [NSNumber numberWithInt:-1], nil]; 203 [ThreadUtilities performOnMainThread:@selector(nativeAddMenuAtIndex_OnAppKitThread:) on:self withObject:args waitUntilDone:YES]; 204 [args release]; 205 } 206 } 207 208 // This method is a special case for use by the screen menu bar. 209 // See ScreenMenuBar.java -- used to implement setVisible(boolean) by 210 // removing or adding the menu from the current menu bar's list. 211 -(void) javaAddMenu: (CMenu *)theMenu atIndex:(jint)index { 212 @synchronized(self) { 213 if (index == -1){ 214 [fMenuList addObject:theMenu]; 215 }else{ 216 [fMenuList insertObject:theMenu atIndex:index]; 217 } 218 } 219 220 if (self == sActiveMenuBar) { 221 NSArray *args = [[NSArray alloc] initWithObjects:theMenu, [NSNumber numberWithInt:index], nil]; 222 [ThreadUtilities performOnMainThread:@selector(nativeAddMenuAtIndex_OnAppKitThread:) on:self withObject:args waitUntilDone:YES]; 223 [args release]; 224 } 225 } 226 227 - (NSInteger) javaIndexToNSMenuIndex_OnAppKitThread:(jint)javaIndex { 228 AWT_ASSERT_APPKIT_THREAD; 229 NSInteger returnValue = -1; 230 NSMenu *theMainMenu = [NSApp mainMenu]; 231 232 if (javaIndex == -1) { 233 if (fHelpMenu) { 234 returnValue = [theMainMenu indexOfItemWithSubmenu:[fHelpMenu menu]]; 235 } 236 } else { 237 CMenu *requestedMenu = [fMenuList objectAtIndex:javaIndex]; 238 239 if (requestedMenu == fHelpMenu) { 240 returnValue = [theMainMenu indexOfItemWithSubmenu:[fHelpMenu menu]]; 241 } else { 242 NSUInteger i, menuCount = [theMainMenu numberOfItems]; 243 jint currJavaMenuIndex = 0; 244 for (i = 0; i < menuCount; i++) { 245 NSMenuItem *currItem = [theMainMenu itemAtIndex:i]; 246 NSMenu *currMenu = [currItem submenu]; 247 248 if ([currMenu isJavaMenu]) { 249 if (javaIndex == currJavaMenuIndex) { 250 returnValue = i; 251 break; 252 } 253 254 currJavaMenuIndex++; 255 } 256 } 257 } 258 } 259 260 return returnValue; 261 } 262 263 - (void) nativeAddMenuAtIndex_OnAppKitThread:(NSArray *)args { 264 AWT_ASSERT_APPKIT_THREAD; 265 CMenu *theNewMenu = (CMenu*)[args objectAtIndex:0]; 266 jint index = [(NSNumber*)[args objectAtIndex:1] intValue]; 267 NSApplication *theApp = [NSApplication sharedApplication]; 268 NSMenu *theMainMenu = [theApp mainMenu]; 269 NSMenu *menuToAdd = [theNewMenu menu]; 270 271 if ([theMainMenu indexOfItemWithSubmenu:menuToAdd] == -1) { 272 NSMenuItem *newItem = [[NSMenuItem alloc] init]; 273 [newItem setSubmenu:[theNewMenu menu]]; 274 [newItem setTitle:[[theNewMenu menu] title]]; 275 276 NSInteger nsMenuIndex = [self javaIndexToNSMenuIndex_OnAppKitThread:index]; 277 278 if (nsMenuIndex == -1) { 279 [theMainMenu addItem:newItem]; 280 } else { 281 [theMainMenu insertItem:newItem atIndex:nsMenuIndex]; 282 } 283 284 BOOL newEnabledState = [theNewMenu isEnabled] && !fModallyDisabled; 285 [newItem setEnabled:newEnabledState]; 286 [newItem release]; 287 } 288 } 289 290 - (void) javaDeleteMenu: (jint)index { 291 if (self == sActiveMenuBar) { 292 [ThreadUtilities performOnMainThread:@selector(nativeDeleteMenu_OnAppKitThread:) on:self withObject:[NSNumber numberWithInt:index] waitUntilDone:YES]; 293 } 294 295 @synchronized(self) { 296 CMenu *menuToRemove = [fMenuList objectAtIndex:index]; 297 298 if (menuToRemove == fHelpMenu) { 299 [fHelpMenu release]; 300 fHelpMenu = nil; 301 } 302 303 [fMenuList removeObjectAtIndex:index]; 304 } 305 } 306 307 - (void) nativeDeleteMenu_OnAppKitThread:(id)indexObj { 308 AWT_ASSERT_APPKIT_THREAD; 309 NSApplication *theApp = [NSApplication sharedApplication]; 310 NSMenu *theMainMenu = [theApp mainMenu]; 311 jint menuToRemove = [(NSNumber *)indexObj intValue]; 312 NSInteger nsMenuToRemove = [self javaIndexToNSMenuIndex_OnAppKitThread:menuToRemove]; 313 314 if (nsMenuToRemove != -1) { 315 [theMainMenu removeItemAtIndex:nsMenuToRemove]; 316 } 317 } 318 319 - (void) javaSetHelpMenu:(CMenu *)theMenu { 320 @synchronized(self) { 321 [theMenu retain]; 322 [fHelpMenu release]; 323 fHelpMenu = theMenu; 324 } 325 } 326 327 + (void) addDefaultHelpMenu { 328 AWT_ASSERT_APPKIT_THREAD; 329 330 // Look for a help book tag. If it's there, add the help menu. 331 @synchronized ([CMenuBar class]) { 332 if (!sSetupHelpMenu) { 333 if (sDefaultHelpMenu == nil) { 334 // If we are embedded, don't make a help menu. 335 // TODO(cpc): we don't have NSApplicationAWT yet... 336 //if (![NSApp isKindOfClass:[NSApplicationAWT class]]) { 337 // sSetupHelpMenu = YES; 338 // return; 339 //} 340 341 // If the developer specified a NIB, don't make a help menu. 342 // TODO(cpc): usingDefaultNib only defined on NSApplicationAWT 343 //if (![NSApp usingDefaultNib]) { 344 // sSetupHelpMenu = YES; 345 // return; 346 //} 347 348 // TODO: not implemented 349 } 350 351 sSetupHelpMenu = YES; 352 } 353 } 354 355 if (sDefaultHelpMenu) { 356 NSMenu *theMainMenu = [NSApp mainMenu]; 357 358 if ([theMainMenu indexOfItemWithSubmenu:sDefaultHelpMenu] == -1) { 359 // Since we're re-using this NSMenu, we need to clear its parent before 360 // adding it to a new menu item, or else AppKit will complain. 361 [sDefaultHelpMenu setSupermenu:nil]; 362 363 // Add the help menu to the main menu. 364 NSMenuItem *newItem = [[NSMenuItem alloc] init]; 365 [newItem setSubmenu:sDefaultHelpMenu]; 366 [newItem setTitle:[sDefaultHelpMenu title]]; 367 [theMainMenu addItem:newItem]; 368 369 // Release it so the main menu owns it. 370 [newItem release]; 371 } 372 } 373 } 374 375 @end 376 377 /* 378 * Class: sun_lwawt_macosx_CMenuBar 379 * Method: nativeCreateMenuBar 380 * Signature: ()J 381 */ 382 JNIEXPORT jlong JNICALL 383 Java_sun_lwawt_macosx_CMenuBar_nativeCreateMenuBar 384 (JNIEnv *env, jobject peer) 385 { 386 __block CMenuBar *aCMenuBar = nil; 387 JNF_COCOA_ENTER(env); 388 389 jobject cPeerObjGlobal = (*env)->NewGlobalRef(env, peer); 390 391 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ 392 393 aCMenuBar = [[CMenuBar alloc] initWithPeer:cPeerObjGlobal]; 394 // the aCMenuBar is released in CMenuComponent.dispose() 395 }]; 396 if (aCMenuBar == nil) { 397 return 0L; 398 } 399 400 JNF_COCOA_EXIT(env); 401 return ptr_to_jlong(aCMenuBar); 402 } 403 404 /* 405 * Class: sun_lwawt_macosx_CMenuBar 406 * Method: nativeAddAtIndex 407 * Signature: (JJI)V 408 */ 409 JNIEXPORT void JNICALL 410 Java_sun_lwawt_macosx_CMenuBar_nativeAddAtIndex 411 (JNIEnv *env, jobject peer, 412 jlong menuBarObject, jlong menuObject, jint index) 413 { 414 JNF_COCOA_ENTER(env); 415 // Remove the specified item. 416 [((CMenuBar *) jlong_to_ptr(menuBarObject)) javaAddMenu:(CMenu *) jlong_to_ptr(menuObject) atIndex:index]; 417 JNF_COCOA_EXIT(env); 418 } 419 420 /* 421 * Class: sun_lwawt_macosx_CMenuBar 422 * Method: nativeDelMenu 423 * Signature: (JI)V 424 */ 425 JNIEXPORT void JNICALL 426 Java_sun_lwawt_macosx_CMenuBar_nativeDelMenu 427 (JNIEnv *env, jobject peer, jlong menuBarObject, jint index) 428 { 429 JNF_COCOA_ENTER(env); 430 // Remove the specified item. 431 [((CMenuBar *) jlong_to_ptr(menuBarObject)) javaDeleteMenu: index]; 432 JNF_COCOA_EXIT(env); 433 } 434 435 /* 436 * Class: sun_lwawt_macosx_CMenuBar 437 * Method: nativeSetHelpMenu 438 * Signature: (JJ)V 439 */ 440 JNIEXPORT void JNICALL 441 Java_sun_lwawt_macosx_CMenuBar_nativeSetHelpMenu 442 (JNIEnv *env, jobject peer, jlong menuBarObject, jlong menuObject) 443 { 444 JNF_COCOA_ENTER(env); 445 // Remove the specified item. 446 [((CMenuBar *) jlong_to_ptr(menuBarObject)) javaSetHelpMenu: ((CMenu *)jlong_to_ptr(menuObject))]; 447 JNF_COCOA_EXIT(env); 448 }