1 /* 2 * Copyright (c) 2011, 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 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 [theMainMenu removeItemAtIndex:[[removedMenuArray objectAtIndex:(removedIndex-1)] integerValue]]; 158 } 159 160 i = cmenuIndex; 161 162 // Add all of the menus in the menu list. 163 for (; i < newMenuListSize; i++) { 164 CMenu *newMenu = (CMenu *)[menubar->fMenuList objectAtIndex:i]; 165 166 if (newMenu != menubar->fHelpMenu) { 167 NSArray *args = [NSArray arrayWithObjects:newMenu, [NSNumber numberWithInt:-1], nil]; 168 [menubar nativeAddMenuAtIndex_OnAppKitThread:args]; 169 } 170 } 171 172 // Add the help menu last. 173 if (menubar->fHelpMenu) { 174 NSArray *args = [NSArray arrayWithObjects:menubar->fHelpMenu, [NSNumber numberWithInt:-1], nil]; 175 [menubar nativeAddMenuAtIndex_OnAppKitThread:args]; 176 } else { 177 [CMenuBar addDefaultHelpMenu]; 178 } 179 } 180 181 -(void) deactivate { 182 AWT_ASSERT_APPKIT_THREAD; 183 184 @synchronized([CMenuBar class]) { 185 sActiveMenuBar = nil; 186 } 187 188 @synchronized(self) { 189 fModallyDisabled = NO; 190 } 191 } 192 193 -(void) javaAddMenu: (CMenu *)theMenu { 194 @synchronized(self) { 195 [fMenuList addObject: theMenu]; 196 } 197 198 if (self == sActiveMenuBar) { 199 NSArray *args = [[NSArray alloc] initWithObjects:theMenu, [NSNumber numberWithInt:-1], nil]; 200 [ThreadUtilities performOnMainThread:@selector(nativeAddMenuAtIndex_OnAppKitThread:) onObject:self withObject:args waitUntilDone:YES awtMode:YES]; 201 [args release]; 202 } 203 } 204 205 // This method is a special case for use by the screen menu bar. 206 // See ScreenMenuBar.java -- used to implement setVisible(boolean) by 207 // removing or adding the menu from the current menu bar's list. 208 -(void) javaAddMenu: (CMenu *)theMenu atIndex:(jint)index { 209 @synchronized(self) { 210 if (index == -1){ 211 [fMenuList addObject:theMenu]; 212 }else{ 213 [fMenuList insertObject:theMenu atIndex:index]; 214 } 215 } 216 217 if (self == sActiveMenuBar) { 218 NSArray *args = [[NSArray alloc] initWithObjects:theMenu, [NSNumber numberWithInt:index], nil]; 219 [ThreadUtilities performOnMainThread:@selector(nativeAddMenuAtIndex_OnAppKitThread:) onObject:self withObject:args waitUntilDone:YES awtMode:YES]; 220 [args release]; 221 } 222 } 223 224 - (NSInteger) javaIndexToNSMenuIndex_OnAppKitThread:(jint)javaIndex { 225 AWT_ASSERT_APPKIT_THREAD; 226 NSInteger returnValue = -1; 227 NSMenu *theMainMenu = [NSApp mainMenu]; 228 229 if (javaIndex == -1) { 230 if (fHelpMenu) { 231 returnValue = [theMainMenu indexOfItemWithSubmenu:[fHelpMenu menu]]; 232 } 233 } else { 234 CMenu *requestedMenu = [fMenuList objectAtIndex:javaIndex]; 235 236 if (requestedMenu == fHelpMenu) { 237 returnValue = [theMainMenu indexOfItemWithSubmenu:[fHelpMenu menu]]; 238 } else { 239 NSUInteger i, menuCount = [theMainMenu numberOfItems]; 240 jint currJavaMenuIndex = 0; 241 for (i = 0; i < menuCount; i++) { 242 NSMenuItem *currItem = [theMainMenu itemAtIndex:i]; 243 NSMenu *currMenu = [currItem submenu]; 244 245 if ([currMenu isJavaMenu]) { 246 if (javaIndex == currJavaMenuIndex) { 247 returnValue = i; 248 break; 249 } 250 251 currJavaMenuIndex++; 252 } 253 } 254 } 255 } 256 257 return returnValue; 258 } 259 260 - (void) nativeAddMenuAtIndex_OnAppKitThread:(NSArray *)args { 261 AWT_ASSERT_APPKIT_THREAD; 262 CMenu *theNewMenu = (CMenu*)[args objectAtIndex:0]; 263 jint index = [(NSNumber*)[args objectAtIndex:1] intValue]; 264 NSApplication *theApp = [NSApplication sharedApplication]; 265 NSMenu *theMainMenu = [theApp mainMenu]; 266 NSMenu *menuToAdd = [theNewMenu menu]; 267 268 if ([theMainMenu indexOfItemWithSubmenu:menuToAdd] == -1) { 269 NSMenuItem *newItem = [[NSMenuItem alloc] init]; 270 [newItem setSubmenu:[theNewMenu menu]]; 271 [newItem setTitle:[[theNewMenu menu] title]]; 272 273 NSInteger nsMenuIndex = [self javaIndexToNSMenuIndex_OnAppKitThread:index]; 274 275 if (nsMenuIndex == -1) { 276 [theMainMenu addItem:newItem]; 277 } else { 278 [theMainMenu insertItem:newItem atIndex:nsMenuIndex]; 279 } 280 281 BOOL newEnabledState = [theNewMenu isEnabled] && !fModallyDisabled; 282 [newItem setEnabled:newEnabledState]; 283 [newItem release]; 284 } 285 } 286 287 - (void) javaDeleteMenu: (jint)index { 288 if (self == sActiveMenuBar) { 289 [ThreadUtilities performOnMainThread:@selector(nativeDeleteMenu_OnAppKitThread:) onObject:self withObject:[NSNumber numberWithInt:index] waitUntilDone:YES awtMode:YES]; 290 } 291 292 @synchronized(self) { 293 CMenu *menuToRemove = [fMenuList objectAtIndex:index]; 294 295 if (menuToRemove == fHelpMenu) { 296 [fHelpMenu release]; 297 fHelpMenu = nil; 298 } 299 300 [fMenuList removeObjectAtIndex:index]; 301 } 302 } 303 304 - (void) nativeDeleteMenu_OnAppKitThread:(id)indexObj { 305 AWT_ASSERT_APPKIT_THREAD; 306 NSApplication *theApp = [NSApplication sharedApplication]; 307 NSMenu *theMainMenu = [theApp mainMenu]; 308 jint menuToRemove = [(NSNumber *)indexObj intValue]; 309 NSInteger nsMenuToRemove = [self javaIndexToNSMenuIndex_OnAppKitThread:menuToRemove]; 310 311 if (nsMenuToRemove != -1) { 312 [theMainMenu removeItemAtIndex:nsMenuToRemove]; 313 } 314 } 315 316 - (void) javaSetHelpMenu:(CMenu *)theMenu { 317 @synchronized(self) { 318 [theMenu retain]; 319 [fHelpMenu release]; 320 fHelpMenu = theMenu; 321 } 322 } 323 324 + (void) addDefaultHelpMenu { 325 AWT_ASSERT_APPKIT_THREAD; 326 327 // Look for a help book tag. If it's there, add the help menu. 328 @synchronized ([CMenuBar class]) { 329 if (!sSetupHelpMenu) { 330 if (sDefaultHelpMenu == nil) { 331 // If we are embedded, don't make a help menu. 332 // TODO(cpc): we don't have NSApplicationAWT yet... 333 //if (![NSApp isKindOfClass:[NSApplicationAWT class]]) { 334 // sSetupHelpMenu = YES; 335 // return; 336 //} 337 338 // If the developer specified a NIB, don't make a help menu. 339 // TODO(cpc): usingDefaultNib only defined on NSApplicationAWT 340 //if (![NSApp usingDefaultNib]) { 341 // sSetupHelpMenu = YES; 342 // return; 343 //} 344 345 // TODO: not implemented 346 } 347 348 sSetupHelpMenu = YES; 349 } 350 } 351 352 if (sDefaultHelpMenu) { 353 NSMenu *theMainMenu = [NSApp mainMenu]; 354 355 if ([theMainMenu indexOfItemWithSubmenu:sDefaultHelpMenu] == -1) { 356 // Since we're re-using this NSMenu, we need to clear its parent before 357 // adding it to a new menu item, or else AppKit will complain. 358 [sDefaultHelpMenu setSupermenu:nil]; 359 360 // Add the help menu to the main menu. 361 NSMenuItem *newItem = [[NSMenuItem alloc] init]; 362 [newItem setSubmenu:sDefaultHelpMenu]; 363 [newItem setTitle:[sDefaultHelpMenu title]]; 364 [theMainMenu addItem:newItem]; 365 366 // Release it so the main menu owns it. 367 [newItem release]; 368 } 369 } 370 } 371 372 @end 373 374 /* 375 * Class: sun_lwawt_macosx_CMenuBar 376 * Method: nativeCreateMenuBar 377 * Signature: ()J 378 */ 379 JNIEXPORT jlong JNICALL 380 Java_sun_lwawt_macosx_CMenuBar_nativeCreateMenuBar 381 (JNIEnv *env, jobject peer) 382 { 383 CMenuBar *aCMenuBar = nil; 384 JNF_COCOA_ENTER(env); 385 386 jobject cPeerObjGlobal = (*env)->NewGlobalRef(env, peer); 387 388 // We use an array here only to be able to get a return value 389 NSMutableArray *args = [[NSMutableArray alloc] initWithObjects:[NSValue valueWithBytes:&cPeerObjGlobal objCType:@encode(jobject)], nil]; 390 391 [ThreadUtilities performOnMainThread:@selector(_create_OnAppKitThread:) onObject:[CMenuBar alloc] withObject:args waitUntilDone:YES awtMode:YES]; 392 393 aCMenuBar = (CMenuBar *)[args objectAtIndex: 0]; 394 395 if (aCMenuBar == nil) { 396 return 0L; 397 } 398 399 // [args release]; 400 401 // A strange memory managment after that. 402 403 404 JNF_COCOA_EXIT(env); 405 if (aCMenuBar) { 406 CFRetain(aCMenuBar); // GC 407 [aCMenuBar release]; 408 } 409 return ptr_to_jlong(aCMenuBar); 410 } 411 412 /* 413 * Class: sun_lwawt_macosx_CMenuBar 414 * Method: nativeAddAtIndex 415 * Signature: (JJI)V 416 */ 417 JNIEXPORT void JNICALL 418 Java_sun_lwawt_macosx_CMenuBar_nativeAddAtIndex 419 (JNIEnv *env, jobject peer, 420 jlong menuBarObject, jlong menuObject, jint index) 421 { 422 JNF_COCOA_ENTER(env); 423 // Remove the specified item. 424 [((CMenuBar *) jlong_to_ptr(menuBarObject)) javaAddMenu:(CMenu *) jlong_to_ptr(menuObject) atIndex:index]; 425 JNF_COCOA_EXIT(env); 426 } 427 428 /* 429 * Class: sun_lwawt_macosx_CMenuBar 430 * Method: nativeDelMenu 431 * Signature: (JI)V 432 */ 433 JNIEXPORT void JNICALL 434 Java_sun_lwawt_macosx_CMenuBar_nativeDelMenu 435 (JNIEnv *env, jobject peer, jlong menuBarObject, jint index) 436 { 437 JNF_COCOA_ENTER(env); 438 // Remove the specified item. 439 [((CMenuBar *) jlong_to_ptr(menuBarObject)) javaDeleteMenu: index]; 440 JNF_COCOA_EXIT(env); 441 } 442 443 /* 444 * Class: sun_lwawt_macosx_CMenuBar 445 * Method: nativeSetHelpMenu 446 * Signature: (JJ)V 447 */ 448 JNIEXPORT void JNICALL 449 Java_sun_lwawt_macosx_CMenuBar_nativeSetHelpMenu 450 (JNIEnv *env, jobject peer, jlong menuBarObject, jlong menuObject) 451 { 452 JNF_COCOA_ENTER(env); 453 // Remove the specified item. 454 [((CMenuBar *) jlong_to_ptr(menuBarObject)) javaSetHelpMenu: ((CMenu *)jlong_to_ptr(menuObject))]; 455 JNF_COCOA_EXIT(env); 456 }