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