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" */