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