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