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