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