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