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_Menu.h"
27#include "awt_MenuBar.h"
28#include "awt_Frame.h"
29#include <java_awt_Menu.h>
30#include <sun_awt_windows_WMenuPeer.h>
31#include <java_awt_MenuBar.h>
32#include <sun_awt_windows_WMenuBarPeer.h>
33
34/* IMPORTANT! Read the README.JNI file for notes on JNI converted AWT code.
35 */
36
37/***********************************************************************/
38// struct for _DelItem() method
39struct DelItemStruct {
40    jobject menuitem;
41    jint index;
42};
43
44/************************************************************************
45 * AwtMenuItem fields
46 */
47
48jmethodID AwtMenu::countItemsMID;
49jmethodID AwtMenu::getItemMID;
50
51
52/************************************************************************
53 * AwtMenuItem methods
54 */
55
56AwtMenu::AwtMenu() {
57    m_hMenu = NULL;
58}
59
60AwtMenu::~AwtMenu()
61{
62}
63
64void AwtMenu::Dispose()
65{
66    if (m_hMenu != NULL) {
67        /*
68         * Don't verify -- may not be a valid anymore if its window
69         * was disposed of first.
70         */
71        ::DestroyMenu(m_hMenu);
72        m_hMenu = NULL;
73    }
74    AwtMenuItem::Dispose();
75}
76
77LPCTSTR AwtMenu::GetClassName() {
78    return TEXT("SunAwtMenu");
79}
80
81/* Create a new AwtMenu object and menu.   */
82AwtMenu* AwtMenu::Create(jobject self, jobject parent)
83{
84    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
85
86    jobject target = NULL;
87    AwtMenu* menu = NULL;
88
89    try {
90        if (env->EnsureLocalCapacity(1) < 0) {
91            return NULL;
92        }
93
94        JNI_CHECK_NULL_GOTO(parent, "peer", done);
95        AwtMenu* parentMenu = (AwtMenu*) JNI_GET_PDATA(parent);
96
97        target = env->GetObjectField(self, AwtObject::targetID);
98        JNI_CHECK_NULL_GOTO(target, "null target", done);
99
100        menu = new AwtMenu();
101
102        SetLastError(0);
103        HMENU hMenu = ::CreateMenu();
104        // fix for 5088782
105        if (!CheckMenuCreation(env, self, hMenu))
106        {
107            env->DeleteLocalRef(target);
108            return NULL;
109        }
110
111        menu->SetHMenu(hMenu);
112
113        menu->LinkObjects(env, self);
114        menu->SetMenuContainer(parentMenu);
115        if (parentMenu != NULL) {
116            parentMenu->AddItem(menu);
117        }
118    } catch (...) {
119        env->DeleteLocalRef(target);
120        throw;
121    }
122
123done:
124    if (target != NULL) {
125        env->DeleteLocalRef(target);
126    }
127
128    return menu;
129}
130
131void AwtMenu::_AddSeparator(void *param)
132{
133    if (AwtToolkit::IsMainThread()) {
134        JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
135
136        jobject self = (jobject)param;
137        AwtMenu *m = NULL;
138        PDATA pData;
139        JNI_CHECK_PEER_GOTO(self, ret);
140        m = (AwtMenu *)pData;
141        m->AddSeparator();
142ret:
143        env->DeleteGlobalRef(self);
144    } else {
145        AwtToolkit::GetInstance().InvokeFunction(AwtMenu::_AddSeparator, param);
146    }
147}
148
149void AwtMenu::_DelItem(void *param)
150{
151    if (AwtToolkit::IsMainThread()) {
152        JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
153
154        DelItemStruct *dis = (DelItemStruct*) param;
155        jobject self = dis->menuitem;
156        jint index = dis->index;
157
158        AwtMenu *m = NULL;
159        PDATA pData;
160        JNI_CHECK_PEER_GOTO(self, ret);
161        m = (AwtMenu *)pData;
162        m->DeleteItem(static_cast<UINT>(index));
163ret:
164        env->DeleteGlobalRef(self);
165        delete dis;
166    } else {
167        AwtToolkit::GetInstance().InvokeFunction(AwtMenu::_DelItem, param);
168    }
169}
170
171void AwtMenu::UpdateLayout()
172{
173    UpdateLayout(GetHMenu());
174    RedrawMenuBar();
175}
176
177void AwtMenu::UpdateLayout(const HMENU hmenu)
178{
179    const int nMenuItemCount = ::GetMenuItemCount(hmenu);
180    static MENUITEMINFO  mii;
181    for (int idx = 0; idx < nMenuItemCount; ++idx) {
182        memset(&mii, 0, sizeof(mii));
183        mii.cbSize = sizeof(mii);
184        mii.fMask = MIIM_CHECKMARKS | MIIM_DATA | MIIM_ID
185                  | MIIM_STATE | MIIM_SUBMENU | MIIM_TYPE;
186        if (::GetMenuItemInfo(hmenu, idx, TRUE, &mii)) {
187            VERIFY(::RemoveMenu(hmenu, idx, MF_BYPOSITION));
188            VERIFY(::InsertMenuItem(hmenu, idx, TRUE, &mii));
189            if (mii.hSubMenu !=  NULL) {
190                UpdateLayout(mii.hSubMenu);
191            }
192        }
193    }
194}
195
196void AwtMenu::UpdateContainerLayout()
197{
198    AwtMenu* menu = GetMenuContainer();
199    if (menu != NULL) {
200        menu->UpdateLayout();
201    } else {
202        UpdateLayout();
203    }
204}
205
206AwtMenuBar* AwtMenu::GetMenuBar() {
207    return (GetMenuContainer() == NULL) ? NULL : GetMenuContainer()->GetMenuBar();
208}
209
210HWND AwtMenu::GetOwnerHWnd() {
211    return (GetMenuContainer() == NULL) ? NULL : GetMenuContainer()->GetOwnerHWnd();
212}
213
214void AwtMenu::AddSeparator() {
215    VERIFY(::AppendMenu(GetHMenu(), MF_SEPARATOR, 0, 0));
216}
217
218void AwtMenu::AddItem(AwtMenuItem* item)
219{
220    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
221    if (env->EnsureLocalCapacity(2) < 0) {
222        return;
223    }
224
225    if (item->IsSeparator()) {
226        AddSeparator();
227    } else {
228        /* jitem is a java.awt.MenuItem */
229        jobject jitem = item->GetTarget(env);
230
231        jboolean enabled =
232            (jboolean)env->GetBooleanField(jitem, AwtMenuItem::enabledID);
233
234        UINT flags = MF_STRING | (enabled ? MF_ENABLED : MF_GRAYED);
235        flags |= MF_OWNERDRAW;
236        LPCTSTR itemInfo = (LPCTSTR) this;
237
238        if (_tcscmp(item->GetClassName(), TEXT("SunAwtMenu")) == 0) {
239            flags |= MF_POPUP;
240            itemInfo = (LPCTSTR) item;
241        }
242
243        VERIFY(::AppendMenu(GetHMenu(), flags, item->GetID(), itemInfo));
244        if (GetRTL()) {
245            MENUITEMINFO  mif;
246            memset(&mif, 0, sizeof(MENUITEMINFO));
247            mif.cbSize = sizeof(MENUITEMINFO);
248            mif.fMask = MIIM_TYPE;
249            ::GetMenuItemInfo(GetHMenu(), item->GetID(), FALSE, &mif);
250            mif.fType |= MFT_RIGHTJUSTIFY | MFT_RIGHTORDER;
251            ::SetMenuItemInfo(GetHMenu(), item->GetID(), FALSE, &mif);
252        }
253
254        env->DeleteLocalRef(jitem);
255    }
256}
257
258void AwtMenu::DeleteItem(UINT index)
259{
260    VERIFY(::RemoveMenu(GetHMenu(), index, MF_BYPOSITION));
261}
262
263void AwtMenu::SendDrawItem(AwtMenuItem* awtMenuItem,
264                           DRAWITEMSTRUCT& drawInfo)
265{
266    awtMenuItem->DrawItem(drawInfo);
267}
268
269void AwtMenu::SendMeasureItem(AwtMenuItem* awtMenuItem,
270                              HDC hDC, MEASUREITEMSTRUCT& measureInfo)
271{
272    awtMenuItem->MeasureItem(hDC, measureInfo);
273}
274
275int AwtMenu::CountItem(jobject target)
276{
277    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
278    jint nCount = env->CallIntMethod(target, AwtMenu::countItemsMID);
279    DASSERT(!safe_ExceptionOccurred(env));
280    return nCount;
281}
282
283AwtMenuItem* AwtMenu::GetItem(jobject target, jint index)
284{
285    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
286    if (env->EnsureLocalCapacity(2) < 0) {
287        return NULL;
288    }
289    jobject menuItem = env->CallObjectMethod(target, AwtMenu::getItemMID,
290                                             index);
291    if (!menuItem) return NULL; // menu item was removed concurrently
292    DASSERT(!safe_ExceptionOccurred(env));
293
294    jobject wMenuItemPeer = GetPeerForTarget(env, menuItem);
295
296    PDATA pData;
297    AwtMenuItem* awtMenuItem = NULL;
298
299    JNI_CHECK_PEER_GOTO(wMenuItemPeer, done);
300    awtMenuItem = (AwtMenuItem*)pData;
301
302 done:
303    env->DeleteLocalRef(menuItem);
304    env->DeleteLocalRef(wMenuItemPeer);
305
306    return awtMenuItem;
307}
308
309void AwtMenu::DrawItems(DRAWITEMSTRUCT& drawInfo)
310{
311    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
312    if (env->EnsureLocalCapacity(1) < 0) {
313        return;
314    }
315    /* target is a java.awt.Menu */
316    jobject target = GetTarget(env);
317    if(!target || env->ExceptionCheck()) return;
318    int nCount = CountItem(target);
319    for (int i = 0; i < nCount && !env->ExceptionCheck(); i++) {
320        AwtMenuItem* awtMenuItem = GetItem(target, i);
321        if (awtMenuItem != NULL) {
322            SendDrawItem(awtMenuItem, drawInfo);
323        }
324    }
325    env->DeleteLocalRef(target);
326}
327
328void AwtMenu::DrawItem(DRAWITEMSTRUCT& drawInfo)
329{
330    DASSERT(drawInfo.CtlType == ODT_MENU);
331
332    if (drawInfo.itemID == GetID()) {
333        DrawSelf(drawInfo);
334        return;
335    }
336    DrawItems(drawInfo);
337}
338
339void AwtMenu::MeasureItems(HDC hDC, MEASUREITEMSTRUCT& measureInfo)
340{
341    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
342    if (env->EnsureLocalCapacity(1) < 0) {
343        return;
344    }
345   /* target is a java.awt.Menu */
346    jobject target = GetTarget(env);
347    if(!target || env->ExceptionCheck()) return;
348    int nCount = CountItem(target);
349    for (int i = 0; i < nCount && !env->ExceptionCheck(); i++) {
350        AwtMenuItem* awtMenuItem = GetItem(target, i);
351        if (awtMenuItem != NULL) {
352            SendMeasureItem(awtMenuItem, hDC, measureInfo);
353        }
354    }
355    env->DeleteLocalRef(target);
356}
357
358void AwtMenu::MeasureItem(HDC hDC, MEASUREITEMSTRUCT& measureInfo)
359{
360    DASSERT(measureInfo.CtlType == ODT_MENU);
361
362    if (measureInfo.itemID == GetID()) {
363        MeasureSelf(hDC, measureInfo);
364        return;
365    }
366
367    MeasureItems(hDC, measureInfo);
368}
369
370BOOL AwtMenu::IsTopMenu()
371{
372    return (GetMenuBar() == GetMenuContainer());
373}
374
375/************************************************************************
376 * WMenuPeer native methods
377 */
378
379extern "C" {
380
381JNIEXPORT void JNICALL
382Java_java_awt_Menu_initIDs(JNIEnv *env, jclass cls)
383{
384    TRY;
385
386    AwtMenu::countItemsMID = env->GetMethodID(cls, "countItemsImpl", "()I");
387    DASSERT(AwtMenu::countItemsMID != NULL);
388    CHECK_NULL(AwtMenu::countItemsMID);
389
390    AwtMenu::getItemMID = env->GetMethodID(cls, "getItemImpl",
391                                           "(I)Ljava/awt/MenuItem;");
392    DASSERT(AwtMenu::getItemMID != NULL);
393
394    CATCH_BAD_ALLOC;
395}
396
397} /* extern "C" */
398
399
400/************************************************************************
401 * WMenuPeer native methods
402 */
403
404extern "C" {
405
406/*
407 * Class:     sun_awt_windows_WMenuPeer
408 * Method:    addSeparator
409 * Signature: ()V
410 */
411JNIEXPORT void JNICALL
412Java_sun_awt_windows_WMenuPeer_addSeparator(JNIEnv *env, jobject self)
413{
414    TRY;
415
416    jobject selfGlobalRef = env->NewGlobalRef(self);
417
418    AwtToolkit::GetInstance().SyncCall(AwtMenu::_AddSeparator, selfGlobalRef);
419    // selfGlobalRef is deleted in _AddSeparator
420
421    CATCH_BAD_ALLOC;
422}
423
424/*
425 * Class:     sun_awt_windows_WMenuPeer
426 * Method:    delItem
427 * Signature: (I)V
428 */
429JNIEXPORT void JNICALL
430Java_sun_awt_windows_WMenuPeer_delItem(JNIEnv *env, jobject self,
431                                       jint index)
432{
433    TRY;
434
435    DelItemStruct *dis = new DelItemStruct;
436    dis->menuitem = env->NewGlobalRef(self);
437    dis->index = index;
438
439    AwtToolkit::GetInstance().SyncCall(AwtMenu::_DelItem, dis);
440    // global refs and dis are deleted in _DelItem
441
442    CATCH_BAD_ALLOC;
443}
444
445/*
446 * Class:     sun_awt_windows_WMenuPeer
447 * Method:    createMenu
448 * Signature: (Lsun/awt/windows/WMenuBarPeer;)V
449 */
450JNIEXPORT void JNICALL
451Java_sun_awt_windows_WMenuPeer_createMenu(JNIEnv *env, jobject self,
452                                          jobject menuBar)
453{
454    TRY;
455
456    AwtToolkit::CreateComponent(self, menuBar,
457                                (AwtToolkit::ComponentFactory)AwtMenu::Create);
458
459    CATCH_BAD_ALLOC;
460}
461
462/*
463 * Class:     sun_awt_windows_WMenuPeer
464 * Method:    createSubMenu
465 * Signature: (Lsun/awt/windows/WMenuPeer;)V
466 */
467JNIEXPORT void JNICALL
468Java_sun_awt_windows_WMenuPeer_createSubMenu(JNIEnv *env, jobject self,
469                                             jobject menu)
470{
471    TRY;
472
473    AwtToolkit::CreateComponent(self, menu,
474                                (AwtToolkit::ComponentFactory)AwtMenu::Create);
475
476    CATCH_BAD_ALLOC;
477}
478
479} /* extern "C" */
480