1 /* 2 * Copyright (c) 1996, 2017, 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 #include "awt.h" 27 #include "awt_MenuItem.h" 28 #include "awt_Menu.h" 29 #include "awt_MenuBar.h" 30 #include "awt_DesktopProperties.h" 31 #include <sun_awt_windows_WCheckboxMenuItemPeer.h> 32 33 // Begin -- Win32 SDK include files 34 #include <tchar.h> 35 #include <imm.h> 36 #include <ime.h> 37 // End -- Win32 SDK include files 38 39 //add for multifont menuitem 40 #include <java_awt_CheckboxMenuItem.h> 41 #include <java_awt_Toolkit.h> 42 #include <java_awt_event_InputEvent.h> 43 44 /* IMPORTANT! Read the README.JNI file for notes on JNI converted AWT code. 45 */ 46 47 /***********************************************************************/ 48 // struct for _SetLabel() method 49 struct SetLabelStruct { 50 jobject menuitem; 51 jstring label; 52 }; 53 // struct for _SetEnable() method 54 struct SetEnableStruct { 55 jobject menuitem; 56 jboolean isEnabled; 57 }; 58 // struct for _setState() method 59 struct SetStateStruct { 60 jobject menuitem; 61 jboolean isChecked; 62 }; 63 /************************************************************************ 64 * AwtMenuItem fields 65 */ 66 67 HBITMAP AwtMenuItem::bmpCheck; 68 jobject AwtMenuItem::systemFont; 69 70 jfieldID AwtMenuItem::labelID; 71 jfieldID AwtMenuItem::enabledID; 72 jfieldID AwtMenuItem::fontID; 73 jfieldID AwtMenuItem::appContextID; 74 jfieldID AwtMenuItem::shortcutLabelID; 75 jfieldID AwtMenuItem::isCheckboxID; 76 jfieldID AwtMenuItem::stateID; 77 78 jmethodID AwtMenuItem::getDefaultFontMID; 79 80 // Added by waleed to initialize the RTL Flags 81 LANGID AwtMenuItem::m_idLang = LOWORD(GetKeyboardLayout(0)); 82 UINT AwtMenuItem::m_CodePage = 83 AwtMenuItem::LangToCodePage(AwtMenuItem::m_idLang); 84 BOOL AwtMenuItem::sm_rtl = PRIMARYLANGID(GetInputLanguage()) == LANG_ARABIC || 85 PRIMARYLANGID(GetInputLanguage()) == LANG_HEBREW; 86 BOOL AwtMenuItem::sm_rtlReadingOrder = 87 PRIMARYLANGID(GetInputLanguage()) == LANG_ARABIC; 88 89 /* 90 * This constant holds width of the default menu 91 * check-mark bitmap for default settings on XP/Vista, 92 * in pixels 93 */ 94 static const int SM_CXMENUCHECK_DEFAULT_ON_XP = 13; 95 static const int SM_CXMENUCHECK_DEFAULT_ON_VISTA = 15; 96 97 /************************************************************************ 98 * AwtMenuItem methods 99 */ 100 101 AwtMenuItem::AwtMenuItem() { 102 m_peerObject = NULL; 103 m_menuContainer = NULL; 104 m_Id = (UINT)-1; 105 m_freeId = FALSE; 106 m_isCheckbox = FALSE; 107 } 108 109 AwtMenuItem::~AwtMenuItem() 110 { 111 } 112 113 void AwtMenuItem::RemoveCmdID() 114 { 115 if (m_freeId) { 116 AwtToolkit::GetInstance().RemoveCmdID( GetID() ); 117 m_freeId = FALSE; 118 } 119 } 120 void AwtMenuItem::Dispose() 121 { 122 RemoveCmdID(); 123 124 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 125 if (m_peerObject != NULL) { 126 JNI_SET_DESTROYED(m_peerObject); 127 JNI_SET_PDATA(m_peerObject, NULL); 128 env->DeleteGlobalRef(m_peerObject); 129 m_peerObject = NULL; 130 } 131 132 AwtObject::Dispose(); 133 } 134 135 LPCTSTR AwtMenuItem::GetClassName() { 136 return TEXT("SunAwtMenuItem"); 137 } 138 // Convert Language ID to CodePage 139 UINT AwtMenuItem::LangToCodePage(LANGID idLang) 140 { 141 TCHAR strCodePage[MAX_ACP_STR_LEN]; 142 // use the LANGID to create a LCID 143 LCID idLocale = MAKELCID(idLang, SORT_DEFAULT); 144 // get the ANSI code page associated with this locale 145 if (GetLocaleInfo(idLocale, LOCALE_IDEFAULTANSICODEPAGE, strCodePage, sizeof(strCodePage)/sizeof(TCHAR)) > 0 ) 146 return _ttoi(strCodePage); 147 else 148 return GetACP(); 149 } 150 151 BOOL AwtMenuItem::CheckMenuCreation(JNIEnv *env, jobject self, HMENU hMenu) 152 { 153 // fix for 5088782 154 // check if CreateMenu() returns not null value and if it does - 155 // create an InternalError or OutOfMemoryError based on GetLastError(). 156 // This error is set to createError field of WObjectPeer and then 157 // checked and thrown in WMenuPeer or WMenuItemPeer constructor. We 158 // can't throw an error here because this code is invoked on Toolkit thread 159 // return TRUE if menu is created successfully, FALSE otherwise 160 if (hMenu == NULL) 161 { 162 DWORD dw = GetLastError(); 163 jobject createError = NULL; 164 if (dw == ERROR_OUTOFMEMORY) 165 { 166 jstring errorMsg = JNU_NewStringPlatform(env, L"too many menu handles"); 167 if (errorMsg == NULL) { 168 throw std::bad_alloc(); 169 } 170 createError = JNU_NewObjectByName(env, "java/lang/OutOfMemoryError", 171 "(Ljava/lang/String;)V", 172 errorMsg); 173 env->DeleteLocalRef(errorMsg); 174 } 175 else 176 { 177 TCHAR *buf; 178 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 179 NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 180 (LPTSTR)&buf, 0, NULL); 181 jstring s = JNU_NewStringPlatform(env, buf); 182 if (s == NULL) { 183 throw std::bad_alloc(); 184 } 185 createError = JNU_NewObjectByName(env, "java/lang/InternalError", 186 "(Ljava/lang/String;)V", s); 187 LocalFree(buf); 188 env->DeleteLocalRef(s); 189 } 190 if (createError == NULL) { 191 throw std::bad_alloc(); 192 } 193 env->SetObjectField(self, AwtObject::createErrorID, createError); 194 env->DeleteLocalRef(createError); 195 return FALSE; 196 } 197 return TRUE; 198 } 199 200 /* 201 * Link the C++, Java peer together 202 */ 203 void AwtMenuItem::LinkObjects(JNIEnv *env, jobject peer) 204 { 205 m_peerObject = env->NewGlobalRef(peer); 206 JNI_SET_PDATA(peer, this); 207 } 208 209 AwtMenuItem* AwtMenuItem::Create(jobject peer, jobject menuPeer) 210 { 211 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 212 213 jobject target = NULL; 214 AwtMenuItem* item = NULL; 215 216 try { 217 if (env->EnsureLocalCapacity(1) < 0) { 218 return NULL; 219 } 220 if (!AwtToolkit::GetInstance().isFreeIDAvailable()) { 221 return NULL; 222 } 223 224 JNI_CHECK_NULL_RETURN_NULL(menuPeer, "peer"); 225 226 /* target is a java.awt.MenuItem */ 227 target = env->GetObjectField(peer, AwtObject::targetID); 228 229 AwtMenu* menu = (AwtMenu *)JNI_GET_PDATA(menuPeer); 230 item = new AwtMenuItem(); 231 jboolean isCheckbox = 232 (jboolean)env->GetBooleanField(peer, AwtMenuItem::isCheckboxID); 233 if (isCheckbox) { 234 item->SetCheckbox(); 235 } 236 237 item->LinkObjects(env, peer); 238 item->SetMenuContainer(menu); 239 item->SetNewID(); 240 if (menu != NULL) { 241 menu->AddItem(item); 242 } 243 } catch (...) { 244 env->DeleteLocalRef(target); 245 throw; 246 } 247 248 env->DeleteLocalRef(target); 249 return item; 250 } 251 252 MsgRouting AwtMenuItem::WmNotify(UINT notifyCode) 253 { 254 return mrDoDefault; 255 } 256 257 // This function returns a local reference 258 jobject 259 AwtMenuItem::GetFont(JNIEnv *env) 260 { 261 jobject self = GetPeer(env); 262 jobject target = env->GetObjectField(self, AwtObject::targetID); 263 jobject font = JNU_CallMethodByName(env, 0, target, "getFont_NoClientCode", "()Ljava/awt/Font;").l; 264 env->DeleteLocalRef(target); 265 if (env->ExceptionCheck()) { 266 throw std::bad_alloc(); 267 } 268 269 if (font == NULL) { 270 font = env->NewLocalRef(GetDefaultFont(env)); 271 if (env->ExceptionCheck()) { 272 throw std::bad_alloc(); 273 } 274 } 275 276 return font; 277 } 278 279 jobject 280 AwtMenuItem::GetDefaultFont(JNIEnv *env) { 281 if (AwtMenuItem::systemFont == NULL) { 282 jclass cls = env->FindClass("sun/awt/windows/WMenuItemPeer"); 283 if (cls == NULL) { 284 throw std::bad_alloc(); 285 } 286 287 AwtMenuItem::systemFont = 288 env->CallStaticObjectMethod(cls, AwtMenuItem::getDefaultFontMID); 289 if (env->ExceptionCheck()) { 290 env->DeleteLocalRef(cls); 291 throw std::bad_alloc(); 292 } 293 294 AwtMenuItem::systemFont = env->NewGlobalRef(AwtMenuItem::systemFont); 295 if (systemFont == NULL) { 296 env->DeleteLocalRef(cls); 297 throw std::bad_alloc(); 298 } 299 } 300 return AwtMenuItem::systemFont; 301 } 302 303 void 304 AwtMenuItem::DrawSelf(DRAWITEMSTRUCT& drawInfo) 305 { 306 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 307 if (env->EnsureLocalCapacity(4) < 0) { 308 return; 309 } 310 311 // self is sun.awt.windows.WMenuItemPeer 312 jobject self = GetPeer(env); 313 314 // target is java.awt.MenuItem 315 jobject target = env->GetObjectField(self, AwtObject::targetID); 316 317 HDC hDC = drawInfo.hDC; 318 RECT rect = drawInfo.rcItem; 319 RECT textRect = rect; 320 SIZE size; 321 322 DWORD crBack,crText; 323 HBRUSH hbrBack; 324 325 jobject font; 326 try { 327 font = GetFont(env); 328 } catch (std::bad_alloc&) { 329 env->DeleteLocalRef(target); 330 throw; 331 } 332 333 jstring text = GetJavaString(env); 334 if (env->ExceptionCheck()) { 335 env->DeleteLocalRef(target); 336 throw std::bad_alloc(); 337 } 338 size = AwtFont::getMFStringSize(hDC, font, text); 339 340 /* 4700350: If the font size is taller than the menubar, change to the 341 * default font. Otherwise, menu text is painted over the title bar and 342 * client area. -bchristi 343 */ 344 if (IsTopMenu() && size.cy > ::GetSystemMetrics(SM_CYMENU)) { 345 env->DeleteLocalRef(font); 346 try { 347 font = env->NewLocalRef(GetDefaultFont(env)); 348 } catch (std::bad_alloc&) { 349 env->DeleteLocalRef(target); 350 env->DeleteLocalRef(text); 351 throw; 352 } 353 size = AwtFont::getMFStringSize(hDC, font, text); 354 } 355 356 /* Fix for bug 4257944 by ssi@sparc.spb.su 357 * check state of the parent 358 */ 359 AwtMenu* menu = GetMenuContainer(); 360 DASSERT(menu != NULL && GetID() >= 0); 361 362 //Check whether the MenuItem is disabled. 363 BOOL bEnabled = (jboolean)env->GetBooleanField(target, 364 AwtMenuItem::enabledID); 365 if (menu != NULL) { 366 bEnabled = bEnabled && !menu->IsDisabledAndPopup(); 367 } 368 369 if ((drawInfo.itemState) & (ODS_SELECTED)) { 370 // Set background and text colors for selected item 371 crBack = ::GetSysColor (COLOR_HIGHLIGHT); 372 // Disabled text must be drawn in gray. 373 crText = ::GetSysColor(bEnabled? COLOR_HIGHLIGHTTEXT : COLOR_GRAYTEXT); 374 } else { 375 // COLOR_MENUBAR is only defined on WindowsXP. Our binaries are 376 // built on NT, hence the below ifdef. 377 378 #ifndef COLOR_MENUBAR 379 #define COLOR_MENUBAR 30 380 #endif 381 // Set background and text colors for unselected item 382 if (IS_WINXP && IsTopMenu() && AwtDesktopProperties::IsXPStyle()) { 383 crBack = ::GetSysColor (COLOR_MENUBAR); 384 } else { 385 crBack = ::GetSysColor (COLOR_MENU); 386 } 387 // Disabled text must be drawn in gray. 388 crText = ::GetSysColor (bEnabled ? COLOR_MENUTEXT : COLOR_GRAYTEXT); 389 } 390 391 // Fill item rectangle with background color 392 hbrBack = ::CreateSolidBrush (crBack); 393 DASSERT(hbrBack); 394 VERIFY(::FillRect (hDC, &rect, hbrBack)); 395 VERIFY(::DeleteObject (hbrBack)); 396 397 // Set current background and text colors 398 ::SetBkColor (hDC, crBack); 399 ::SetTextColor (hDC, crText); 400 401 int nOldBkMode = ::SetBkMode(hDC, OPAQUE); 402 DASSERT(nOldBkMode != 0); 403 404 //draw check mark 405 int checkWidth = ::GetSystemMetrics(SM_CXMENUCHECK); 406 // Workaround for CR#6401956 407 if (IS_WINVISTA) { 408 AdjustCheckWidth(checkWidth); 409 } 410 411 if (IsCheckbox()) { 412 // means that target is a java.awt.CheckboxMenuItem 413 jboolean state = 414 (jboolean)env->GetBooleanField(target, AwtMenuItem::stateID); 415 if (state) { 416 DASSERT(drawInfo.itemState & ODS_CHECKED); 417 RECT checkRect; 418 ::CopyRect(&checkRect, &textRect); 419 if (GetRTL()) 420 checkRect.left = checkRect.right - checkWidth; 421 else 422 checkRect.right = checkRect.left + checkWidth; 423 424 DrawCheck(hDC, checkRect); 425 } 426 } 427 428 ::SetBkMode(hDC, TRANSPARENT); 429 int x = 0; 430 //draw string 431 if (!IsTopMenu()){ 432 textRect.left += checkWidth; 433 x = (GetRTL()) ? textRect.right - checkWidth - size.cx : textRect.left; 434 } else { 435 x = textRect.left = (textRect.left + textRect.right - size.cx) / 2; 436 } 437 438 int y = (textRect.top+textRect.bottom-size.cy)/2; 439 440 // Text must be drawn in emboss if the Menu is disabled and not selected. 441 BOOL bEmboss = !bEnabled && !(drawInfo.itemState & ODS_SELECTED); 442 if (bEmboss) { 443 ::SetTextColor(hDC, GetSysColor(COLOR_BTNHILIGHT)); 444 AwtFont::drawMFString(hDC, font, text, x + 1, y + 1, GetCodePage()); 445 ::SetTextColor(hDC, GetSysColor(COLOR_BTNSHADOW)); 446 } 447 AwtFont::drawMFString(hDC, font, text, x, y, GetCodePage()); 448 449 jstring shortcutLabel = 450 (jstring)env->GetObjectField(self, AwtMenuItem::shortcutLabelID); 451 if (!IsTopMenu() && shortcutLabel != NULL) { 452 UINT oldAlign = 0; 453 if (GetRTL()){ 454 oldAlign = ::SetTextAlign(hDC, TA_LEFT); 455 AwtFont::drawMFString(hDC, font, shortcutLabel, textRect.left, y, 456 GetCodePage()); 457 } else { 458 oldAlign = ::SetTextAlign(hDC, TA_RIGHT); 459 AwtFont::drawMFString(hDC, font, shortcutLabel, 460 textRect.right - checkWidth, y, 461 GetCodePage()); 462 } 463 464 ::SetTextAlign(hDC, oldAlign); 465 } 466 467 VERIFY(::SetBkMode(hDC,nOldBkMode)); 468 469 env->DeleteLocalRef(target); 470 env->DeleteLocalRef(text); 471 env->DeleteLocalRef(font); 472 env->DeleteLocalRef(shortcutLabel); 473 } 474 475 /* 476 * This function helps us to prevent check-mark's 477 * distortion appeared due to changing of default 478 * settings on Vista 479 */ 480 void AwtMenuItem::AdjustCheckWidth(int& checkWidth) 481 { 482 if (checkWidth == SM_CXMENUCHECK_DEFAULT_ON_VISTA) { 483 checkWidth = SM_CXMENUCHECK_DEFAULT_ON_XP; 484 } 485 } 486 487 void AwtMenuItem::DrawItem(DRAWITEMSTRUCT& drawInfo) 488 { 489 DASSERT(drawInfo.CtlType == ODT_MENU); 490 491 if (drawInfo.itemID != m_Id) 492 return; 493 494 DrawSelf(drawInfo); 495 } 496 497 void AwtMenuItem::MeasureSelf(HDC hDC, MEASUREITEMSTRUCT& measureInfo) 498 { 499 JNIEnv *env =(JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 500 if (env->EnsureLocalCapacity(4) < 0) { 501 return; 502 } 503 504 /* self is a sun.awt.windows.WMenuItemPeer */ 505 jobject self = GetPeer(env); 506 507 /* font is a java.awt.Font */ 508 jobject font = GetFont(env); 509 jstring text = GetJavaString(env); 510 if (env->ExceptionCheck()) { 511 env->DeleteLocalRef(font); 512 throw std::bad_alloc(); 513 } 514 SIZE size = AwtFont::getMFStringSize(hDC, font, text); 515 516 /* 4700350: If the font size is taller than the menubar, change to the 517 * default font. Otherwise, menu text is painted over the title bar and 518 * client area. -bchristi 519 */ 520 if (IsTopMenu() && size.cy > ::GetSystemMetrics(SM_CYMENU)) { 521 jobject defFont; 522 try { 523 defFont = GetDefaultFont(env); 524 } catch (std::bad_alloc&) { 525 env->DeleteLocalRef(text); 526 env->DeleteLocalRef(font); 527 throw; 528 } 529 env->DeleteLocalRef(font); 530 font = env->NewLocalRef(defFont); 531 size = AwtFont::getMFStringSize(hDC, font, text); 532 } 533 534 jstring fontName = 535 (jstring)JNU_CallMethodByName(env, 0,font, "getName", 536 "()Ljava/lang/String;").l; 537 if (env->ExceptionCheck()) { 538 env->DeleteLocalRef(text); 539 env->DeleteLocalRef(font); 540 throw std::bad_alloc(); 541 } 542 543 /* fontMetrics is a Hsun_awt_windows_WFontMetrics */ 544 jobject fontMetrics = GetFontMetrics(env, font); 545 if (env->ExceptionCheck()) { 546 env->DeleteLocalRef(text); 547 env->DeleteLocalRef(font); 548 env->DeleteLocalRef(fontName); 549 throw std::bad_alloc(); 550 } 551 552 // int height = env->GetIntField(fontMetrics, AwtFont::heightID); 553 int height = (jint)JNU_CallMethodByName(env, 0, fontMetrics, "getHeight", 554 "()I").i; 555 if (env->ExceptionCheck()) { 556 env->DeleteLocalRef(text); 557 env->DeleteLocalRef(font); 558 env->DeleteLocalRef(fontName); 559 env->DeleteLocalRef(fontMetrics); 560 throw std::bad_alloc(); 561 } 562 563 measureInfo.itemHeight = height; 564 measureInfo.itemHeight += measureInfo.itemHeight/3; 565 // 3 is a heuristic number 566 measureInfo.itemWidth = size.cx; 567 if (!IsTopMenu()) { 568 int checkWidth = ::GetSystemMetrics(SM_CXMENUCHECK); 569 // Workaround for CR#6401956 570 if (IS_WINVISTA) { 571 AdjustCheckWidth(checkWidth); 572 } 573 measureInfo.itemWidth += checkWidth; 574 575 // Add in shortcut width, if one exists. 576 jstring shortcutLabel = 577 (jstring)env->GetObjectField(self, AwtMenuItem::shortcutLabelID); 578 if (shortcutLabel != NULL) { 579 size = AwtFont::getMFStringSize(hDC, font, shortcutLabel); 580 measureInfo.itemWidth += size.cx + checkWidth; 581 env->DeleteLocalRef(shortcutLabel); 582 } 583 } 584 env->DeleteLocalRef(text); 585 env->DeleteLocalRef(font); 586 env->DeleteLocalRef(fontName); 587 env->DeleteLocalRef(fontMetrics); 588 } 589 590 void AwtMenuItem::MeasureItem(HDC hDC, MEASUREITEMSTRUCT& measureInfo) 591 { 592 DASSERT(measureInfo.CtlType == ODT_MENU); 593 594 if (measureInfo.itemID != m_Id) 595 return; 596 597 MeasureSelf(hDC, measureInfo); 598 } 599 600 jobject AwtMenuItem::GetFontMetrics(JNIEnv *env, jobject font) 601 { 602 static jobject toolkit = NULL; 603 if (toolkit == NULL) { 604 if (env->PushLocalFrame(2) < 0) 605 return NULL; 606 jclass cls = env->FindClass("java/awt/Toolkit"); 607 CHECK_NULL_RETURN(cls, NULL); 608 jobject toolkitLocal = 609 env->CallStaticObjectMethod(cls, AwtToolkit::getDefaultToolkitMID); 610 env->DeleteLocalRef(cls); 611 CHECK_NULL_RETURN(toolkitLocal, NULL); 612 toolkit = env->NewGlobalRef(toolkitLocal); 613 env->DeleteLocalRef(toolkitLocal); 614 CHECK_NULL_RETURN(toolkit, NULL); 615 env->PopLocalFrame(0); 616 } 617 /* 618 JNU_PrintClass(env, "toolkit", toolkit); 619 JNU_PrintClass(env, "font", font); 620 621 jclass cls = env->FindClass("java/awt/Toolkit"); 622 jmethodID mid = env->GetMethodID(cls, "getFontMetrics", 623 "(Ljava/awt/Font;)Ljava/awt/FontMetrics;"); 624 jstring fontName = 625 (jstring)JNU_CallMethodByName(env, 0,font, "getName", 626 "()Ljava/lang/String;").l; 627 JNU_PrintString(env, "font name", fontName); 628 629 fprintf(stderr, "mid: %x\n", mid); 630 fprintf(stderr, "cached mid: %x\n", AwtToolkit::getFontMetricsMID); 631 DASSERT(!safe_ExceptionOccurred(env)); 632 */ 633 jobject fontMetrics = 634 env->CallObjectMethod(toolkit, AwtToolkit::getFontMetricsMID, font); 635 DASSERT(!safe_ExceptionOccurred(env)); 636 637 return fontMetrics; 638 } 639 640 BOOL AwtMenuItem::IsTopMenu() 641 { 642 return FALSE; 643 } 644 645 void AwtMenuItem::DrawCheck(HDC hDC, RECT rect) 646 { 647 if (bmpCheck == NULL) { 648 bmpCheck = ::LoadBitmap(AwtToolkit::GetInstance().GetModuleHandle(), 649 TEXT("CHECK_BITMAP")); 650 DASSERT(bmpCheck != NULL); 651 } 652 653 #define BM_SIZE 26 /* height and width of check.bmp */ 654 655 // Square the rectangle, so the check is proportional. 656 int width = rect.right - rect.left; 657 int diff = max(rect.bottom - rect.top - width, 0) ; 658 int bottom = diff / 2; 659 rect.bottom -= bottom; 660 rect.top += diff - bottom; 661 662 HDC hdcBitmap = ::CreateCompatibleDC(hDC); 663 DASSERT(hdcBitmap != NULL); 664 HBITMAP hbmSave = (HBITMAP)::SelectObject(hdcBitmap, bmpCheck); 665 VERIFY(::StretchBlt(hDC, rect.left, rect.top, 666 rect.right - rect.left, rect.bottom - rect.top, 667 hdcBitmap, 0, 0, BM_SIZE, BM_SIZE, SRCCOPY)); 668 ::SelectObject(hdcBitmap, hbmSave); 669 VERIFY(::DeleteDC(hdcBitmap)); 670 } 671 672 void AwtMenuItem::DoCommand() 673 { 674 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 675 676 // peer is sun.awt.windows.WMenuItemPeer 677 jobject peer = GetPeer(env); 678 679 if (IsCheckbox()) { 680 UINT nState = ::GetMenuState(GetMenuContainer()->GetHMenu(), 681 GetID(), MF_BYCOMMAND); 682 DASSERT(nState != 0xFFFFFFFF); 683 DoCallback("handleAction", "(Z)V", ((nState & MF_CHECKED) == 0)); 684 } else { 685 DoCallback("handleAction", "(JI)V", TimeHelper::getMessageTimeUTC(), 686 (jint)AwtComponent::GetJavaModifiers()); 687 } 688 } 689 690 void AwtMenuItem::SetLabel(LPCTSTR sb) 691 { 692 AwtMenu* menu = GetMenuContainer(); 693 /* Fix for bug 4257944 by ssi@sparc.spb.su 694 * check parent 695 */ 696 if (menu == NULL) return; 697 DASSERT(menu != NULL && GetID() >= 0); 698 699 /* 700 * SetMenuItemInfo is replaced by this code for fix bug 4261935 701 */ 702 HMENU hMenu = menu->GetHMenu(); 703 MENUITEMINFO mii, mii1; 704 705 // get full information about menu item 706 memset(&mii, 0, sizeof(MENUITEMINFO)); 707 mii.cbSize = sizeof(MENUITEMINFO); 708 mii.fMask = MIIM_CHECKMARKS | MIIM_DATA | MIIM_ID 709 | MIIM_STATE | MIIM_SUBMENU | MIIM_TYPE; 710 711 ::GetMenuItemInfo(hMenu, GetID(), FALSE, &mii); 712 713 mii.fType = MFT_OWNERDRAW; 714 mii.dwTypeData = (LPTSTR)(*sb); 715 716 // find index by menu item id 717 int nMenuItemCount = ::GetMenuItemCount(hMenu); 718 int idx; 719 for (idx = 0; (idx < nMenuItemCount); idx++) { 720 memset(&mii1, 0, sizeof(MENUITEMINFO)); 721 mii1.cbSize = sizeof mii1; 722 mii1.fMask = MIIM_ID; 723 ::GetMenuItemInfo(hMenu, idx, TRUE, &mii1); 724 if (mii.wID == mii1.wID) break; 725 } 726 727 ::RemoveMenu(hMenu, idx, MF_BYPOSITION); 728 ::InsertMenuItem(hMenu, idx, TRUE, &mii); 729 730 RedrawMenuBar(); 731 } 732 733 void AwtMenuItem::Enable(BOOL isEnabled) 734 { 735 AwtMenu* menu = GetMenuContainer(); 736 /* Fix for bug 4257944 by ssi@sparc.spb.su 737 * check state of the parent 738 */ 739 if (menu == NULL) return; 740 isEnabled = isEnabled && !menu->IsDisabledAndPopup(); 741 DASSERT(menu != NULL && GetID() >= 0); 742 VERIFY(::EnableMenuItem(menu->GetHMenu(), GetID(), 743 MF_BYCOMMAND | (isEnabled ? MF_ENABLED : MF_GRAYED)) 744 != 0xFFFFFFFF); 745 746 RedrawMenuBar(); 747 } 748 749 void AwtMenuItem::SetState(BOOL isChecked) 750 { 751 AwtMenu* menu = GetMenuContainer(); 752 /* Fix for bug 4257944 by ssi@sparc.spb.su 753 * check parent 754 */ 755 if (menu == NULL) return; 756 DASSERT(menu != NULL && GetID() >= 0); 757 VERIFY(::CheckMenuItem(menu->GetHMenu(), GetID(), 758 MF_BYCOMMAND | (isChecked ? MF_CHECKED : MF_UNCHECKED)) 759 != 0xFFFFFFFF); 760 761 RedrawMenuBar(); 762 } 763 764 /** 765 * If the menu changes after the system has created the window, 766 * this function must be called to draw the changed menu bar. 767 */ 768 void AwtMenuItem::RedrawMenuBar() { 769 AwtMenu* menu = GetMenuContainer(); 770 if (menu != NULL && menu->GetMenuBar() == menu){ 771 menu->RedrawMenuBar(); 772 } 773 } 774 775 void AwtMenuItem::UpdateContainerLayout() { 776 AwtMenu* menu = GetMenuContainer(); 777 if (menu != NULL) { 778 DASSERT(menu != NULL && GetID() >= 0); 779 menu->UpdateLayout(); 780 } 781 } 782 783 void AwtMenuItem::_SetLabel(void *param) { 784 if (AwtToolkit::IsMainThread()) { 785 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 786 787 SetLabelStruct *sls = (SetLabelStruct *)param; 788 jobject self = sls->menuitem; 789 jstring label = sls->label; 790 791 int badAlloc = 0; 792 AwtMenuItem *m = NULL; 793 794 PDATA pData; 795 JNI_CHECK_PEER_GOTO(self, ret); 796 m = (AwtMenuItem *)pData; 797 // if (::IsWindow(m->GetOwnerHWnd())) 798 { 799 // fix for bug 4251036 MenuItem setLabel(null/"") behaves differently 800 // under Win32 and Solaris 801 jstring empty = NULL; 802 if (JNU_IsNull(env, label)) 803 { 804 empty = JNU_NewStringPlatform(env, TEXT("")); 805 } 806 if (env->ExceptionCheck()) { 807 badAlloc = 1; 808 goto ret; 809 } 810 LPCTSTR labelPtr; 811 if (empty != NULL) 812 { 813 labelPtr = JNU_GetStringPlatformChars(env, empty, 0); 814 } 815 else 816 { 817 labelPtr = JNU_GetStringPlatformChars(env, label, 0); 818 } 819 if (labelPtr == NULL) 820 { 821 badAlloc = 1; 822 } 823 else 824 { 825 DASSERT(!IsBadStringPtr(labelPtr, 20)); 826 m->SetLabel(labelPtr); 827 if (empty != NULL) 828 { 829 JNU_ReleaseStringPlatformChars(env, empty, labelPtr); 830 } 831 else 832 { 833 JNU_ReleaseStringPlatformChars(env, label, labelPtr); 834 } 835 } 836 if (empty != NULL) 837 { 838 env->DeleteLocalRef(empty); 839 } 840 } 841 842 ret: 843 env->DeleteGlobalRef(self); 844 if (label != NULL) 845 { 846 env->DeleteGlobalRef(label); 847 } 848 849 delete sls; 850 851 if (badAlloc) 852 { 853 throw std::bad_alloc(); 854 } 855 } else { 856 AwtToolkit::GetInstance().InvokeFunction(AwtMenuItem::_SetLabel, param); 857 } 858 } 859 860 void AwtMenuItem::_UpdateLayout(void *param) 861 { 862 if (AwtToolkit::IsMainThread()) { 863 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 864 865 jobject self = (jobject)param; 866 867 AwtMenuItem *m = NULL; 868 869 PDATA pData; 870 JNI_CHECK_PEER_GOTO(self, ret); 871 872 m = (AwtMenuItem *)pData; 873 874 m->UpdateContainerLayout(); 875 ret: 876 env->DeleteGlobalRef(self); 877 } else { 878 AwtToolkit::GetInstance().InvokeFunction(AwtMenuItem::_UpdateLayout, param); 879 } 880 } 881 882 void AwtMenuItem::_SetEnable(void *param) 883 { 884 if (AwtToolkit::IsMainThread()) { 885 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 886 887 SetEnableStruct *ses = (SetEnableStruct*) param; 888 jobject self = ses->menuitem; 889 jboolean isEnabled = ses->isEnabled; 890 891 AwtMenuItem *m = NULL; 892 893 PDATA pData; 894 JNI_CHECK_PEER_GOTO(self, ret); 895 896 m = (AwtMenuItem *)pData; 897 898 m->Enable(isEnabled); 899 ret: 900 env->DeleteGlobalRef(self); 901 delete ses; 902 } else { 903 AwtToolkit::GetInstance().InvokeFunction(AwtMenuItem::_SetEnable, param); 904 } 905 } 906 907 void AwtMenuItem::_SetState(void *param) 908 { 909 if (AwtToolkit::IsMainThread()) { 910 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 911 912 SetStateStruct *sts = (SetStateStruct*) param; 913 jobject self = sts->menuitem; 914 jboolean isChecked = sts->isChecked; 915 916 AwtMenuItem *m = NULL; 917 918 PDATA pData; 919 JNI_CHECK_PEER_GOTO(self, ret); 920 m = (AwtMenuItem *)pData; 921 m->SetState(isChecked); 922 ret: 923 env->DeleteGlobalRef(self); 924 delete sts; 925 } else { 926 AwtToolkit::GetInstance().InvokeFunction(AwtMenuItem::_SetState, param); 927 } 928 } 929 BOOL AwtMenuItem::IsSeparator() { 930 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 931 if (env->EnsureLocalCapacity(2) < 0) { 932 return FALSE; 933 } 934 jobject jitem = GetTarget(env); 935 jstring label = 936 (jstring)(env)->GetObjectField(jitem, AwtMenuItem::labelID); 937 if (label == NULL) { 938 env->DeleteLocalRef(label); 939 env->DeleteLocalRef(jitem); 940 return FALSE; //separator must has '-' as label. 941 } 942 LPCWSTR labelW = JNU_GetStringPlatformChars(env, label, NULL); 943 BOOL isSeparator = (labelW && (wcscmp(labelW, L"-") == 0)); 944 JNU_ReleaseStringPlatformChars(env, label, labelW); 945 946 env->DeleteLocalRef(label); 947 env->DeleteLocalRef(jitem); 948 949 return isSeparator; 950 } 951 952 /************************************************************************ 953 * MenuComponent native methods 954 */ 955 956 extern "C" { 957 958 JNIEXPORT void JNICALL 959 Java_java_awt_MenuComponent_initIDs(JNIEnv *env, jclass cls) 960 { 961 TRY; 962 963 AwtMenuItem::fontID = env->GetFieldID(cls, "font", "Ljava/awt/Font;"); 964 CHECK_NULL(AwtMenuItem::fontID); 965 AwtMenuItem::appContextID = env->GetFieldID(cls, "appContext", "Lsun/awt/AppContext;"); 966 967 CATCH_BAD_ALLOC; 968 } 969 970 } /* extern "C" */ 971 972 973 /************************************************************************ 974 * MenuItem native methods 975 */ 976 977 extern "C" { 978 979 JNIEXPORT void JNICALL 980 Java_java_awt_MenuItem_initIDs(JNIEnv *env, jclass cls) 981 { 982 TRY; 983 984 AwtMenuItem::labelID = env->GetFieldID(cls, "label", "Ljava/lang/String;"); 985 CHECK_NULL(AwtMenuItem::labelID); 986 AwtMenuItem::enabledID = env->GetFieldID(cls, "enabled", "Z"); 987 988 CATCH_BAD_ALLOC; 989 } 990 991 } /* extern "C" */ 992 993 994 /************************************************************************ 995 * CheckboxMenuItem fields 996 */ 997 998 extern "C" { 999 1000 JNIEXPORT void JNICALL 1001 Java_java_awt_CheckboxMenuItem_initIDs(JNIEnv *env, jclass cls) 1002 { 1003 TRY; 1004 1005 AwtMenuItem::stateID = env->GetFieldID(cls, "state", "Z"); 1006 1007 CATCH_BAD_ALLOC; 1008 } 1009 1010 } /* extern "C" */ 1011 1012 1013 /************************************************************************ 1014 * WMenuItemPeer native methods 1015 */ 1016 1017 extern "C" { 1018 1019 /* 1020 * Class: sun_awt_windows_WMenuItemPeer 1021 * Method: initIDs 1022 * Signature: ()V 1023 */ 1024 JNIEXPORT void JNICALL 1025 Java_sun_awt_windows_WMenuItemPeer_initIDs(JNIEnv *env, jclass cls) 1026 { 1027 TRY; 1028 1029 AwtMenuItem::isCheckboxID = env->GetFieldID(cls, "isCheckbox", "Z"); 1030 CHECK_NULL(AwtMenuItem::isCheckboxID); 1031 AwtMenuItem::shortcutLabelID = env->GetFieldID(cls, "shortcutLabel", 1032 "Ljava/lang/String;"); 1033 CHECK_NULL(AwtMenuItem::shortcutLabelID); 1034 AwtMenuItem::getDefaultFontMID = 1035 env->GetStaticMethodID(cls, "getDefaultFont", "()Ljava/awt/Font;"); 1036 1037 CATCH_BAD_ALLOC; 1038 } 1039 1040 /* 1041 * Class: sun_awt_windows_WMenuItemPeer 1042 * Method: _setLabel 1043 * Signature: (Ljava/lang/String;)V 1044 */ 1045 JNIEXPORT void JNICALL 1046 Java_sun_awt_windows_WMenuItemPeer__1setLabel(JNIEnv *env, jobject self, 1047 jstring label) 1048 { 1049 TRY; 1050 1051 SetLabelStruct *sls = new SetLabelStruct; 1052 sls->menuitem = env->NewGlobalRef(self); 1053 sls->label = (label == NULL) ? NULL : (jstring)env->NewGlobalRef(label); 1054 1055 AwtToolkit::GetInstance().SyncCall(AwtMenuItem::_SetLabel, sls); 1056 // global refs and sls are deleted in _SetLabel 1057 1058 CATCH_BAD_ALLOC; 1059 } 1060 1061 /* 1062 * Class: sun_awt_windows_WMenuItemPeer 1063 * Method: _setFont 1064 * Signature: (Ljava/awt/Font;)V 1065 */ 1066 JNIEXPORT void JNICALL 1067 Java_sun_awt_windows_WMenuItemPeer__1setFont(JNIEnv *env, jobject self, jobject) 1068 { 1069 TRY; 1070 1071 jobject selfGlobalRef = env->NewGlobalRef(self); 1072 1073 // Current implementation of AwtMenuItem get font attribute from the peer 1074 // directly, so we ignore it here, but update current menu layout. 1075 AwtToolkit::GetInstance().SyncCall(AwtMenuItem::_UpdateLayout, selfGlobalRef); 1076 // selfGlobalRef is deleted in _UpdateLayout 1077 1078 CATCH_BAD_ALLOC; 1079 } 1080 1081 /* 1082 * Class: sun_awt_windows_WMenuItemPeer 1083 * Method: create 1084 * Signature: (Lsun/awt/windows/WMenuPeer;)V 1085 */ 1086 JNIEXPORT void JNICALL 1087 Java_sun_awt_windows_WMenuItemPeer_create(JNIEnv *env, jobject self, 1088 jobject menu) 1089 { 1090 TRY; 1091 1092 AwtToolkit::CreateComponent(self, menu, 1093 (AwtToolkit::ComponentFactory) 1094 AwtMenuItem::Create); 1095 CATCH_BAD_ALLOC; 1096 } 1097 1098 /* 1099 * Class: sun_awt_windows_WMenuItemPeer 1100 * Method: enable 1101 * Signature: (Z)V 1102 */ 1103 JNIEXPORT void JNICALL 1104 Java_sun_awt_windows_WMenuItemPeer_enable(JNIEnv *env, jobject self, 1105 jboolean on) 1106 { 1107 TRY; 1108 1109 SetEnableStruct *ses = new SetEnableStruct; 1110 ses->menuitem = env->NewGlobalRef(self); 1111 ses->isEnabled = on; 1112 1113 AwtToolkit::GetInstance().SyncCall(AwtMenuItem::_SetEnable, ses); 1114 // global refs and ses are deleted in _SetEnable 1115 1116 CATCH_BAD_ALLOC; 1117 } 1118 1119 /* 1120 * Class: sun_awt_windows_WMenuItemPeer 1121 * Method: _dispose 1122 * Signature: ()V 1123 */ 1124 JNIEXPORT void JNICALL 1125 Java_sun_awt_windows_WMenuItemPeer__1dispose(JNIEnv *env, jobject self) 1126 { 1127 TRY_NO_HANG; 1128 1129 AwtObject::_Dispose(self); 1130 1131 CATCH_BAD_ALLOC; 1132 } 1133 1134 } /* extern "C" */ 1135 1136 /************************************************************************ 1137 * WCheckboxMenuItemPeer native methods 1138 */ 1139 1140 extern "C" { 1141 1142 /* 1143 * Class: sun_awt_windows_WCheckboxMenuItemPeer 1144 * Method: setState 1145 * Signature: (Z)V 1146 */ 1147 JNIEXPORT void JNICALL 1148 Java_sun_awt_windows_WCheckboxMenuItemPeer_setState(JNIEnv *env, jobject self, 1149 jboolean on) 1150 { 1151 TRY; 1152 1153 SetStateStruct *sts = new SetStateStruct; 1154 sts->menuitem = env->NewGlobalRef(self); 1155 sts->isChecked = on; 1156 1157 AwtToolkit::GetInstance().SyncCall(AwtMenuItem::_SetState, sts); 1158 // global refs and sts are deleted in _SetState 1159 1160 CATCH_BAD_ALLOC; 1161 } 1162 1163 } /* extern "C" */