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