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