1/*
2 * Copyright (c) 1996, 2016, 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_PopupMenu.h"
27
28#include "awt_Event.h"
29#include "awt_Window.h"
30
31#include <sun_awt_windows_WPopupMenuPeer.h>
32#include <java_awt_Event.h>
33
34/* IMPORTANT! Read the README.JNI file for notes on JNI converted AWT code.
35 */
36
37/***********************************************************************/
38// struct for _Show method
39struct ShowStruct {
40    jobject self;
41    jobject event;
42};
43
44/************************************************************************
45 * AwtPopupMenu class methods
46 */
47
48AwtPopupMenu::AwtPopupMenu() {
49    m_parent = NULL;
50}
51
52AwtPopupMenu::~AwtPopupMenu()
53{
54}
55
56void AwtPopupMenu::Dispose()
57{
58    m_parent = NULL;
59
60    AwtMenu::Dispose();
61}
62
63LPCTSTR AwtPopupMenu::GetClassName() {
64  return TEXT("SunAwtPopupMenu");
65}
66
67/* Create a new AwtPopupMenu object and menu.   */
68AwtPopupMenu* AwtPopupMenu::Create(jobject self, jobject parent)
69{
70    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
71
72    jobject target = NULL;
73    AwtPopupMenu* popupMenu = NULL;
74
75    try {
76        if (env->EnsureLocalCapacity(1) < 0) {
77            return NULL;
78        }
79
80        JNI_CHECK_NULL_GOTO(parent, "peer", done);
81        AwtComponent* awtParent = (AwtComponent*) JNI_GET_PDATA(parent);
82
83        target = env->GetObjectField(self, AwtObject::targetID);
84        JNI_CHECK_NULL_GOTO(target, "null target", done);
85
86        popupMenu = new AwtPopupMenu();
87
88        SetLastError(0);
89        HMENU hMenu = ::CreatePopupMenu();
90        // fix for 5088782
91        if (!CheckMenuCreation(env, self, hMenu))
92        {
93            env->DeleteLocalRef(target);
94            return NULL;
95        }
96
97        popupMenu->SetHMenu(hMenu);
98
99        popupMenu->LinkObjects(env, self);
100        popupMenu->SetParent(awtParent);
101    } catch (...) {
102        env->DeleteLocalRef(target);
103        throw;
104    }
105
106done:
107    env->DeleteLocalRef(target);
108    return popupMenu;
109}
110
111void AwtPopupMenu::Show(JNIEnv *env, jobject event, BOOL isTrayIconPopup)
112{
113    /*
114     * For not TrayIcon popup.
115     * Convert the event's XY to absolute coordinates.  The XY is
116     * relative to the origin component, which is passed by PopupMenu
117     * as the event's target.
118     */
119    if (env->EnsureLocalCapacity(2) < 0) {
120        return;
121    }
122    jobject origin = (env)->GetObjectField(event, AwtEvent::targetID);
123    jobject peerOrigin = GetPeerForTarget(env, origin);
124    PDATA pData;
125    JNI_CHECK_PEER_GOTO(peerOrigin, done);
126    {
127        AwtComponent* awtOrigin = (AwtComponent*)pData;
128        POINT pt;
129        UINT flags = 0;
130        pt.x = (env)->GetIntField(event, AwtEvent::xID);
131        pt.y = (env)->GetIntField(event, AwtEvent::yID);
132
133        if (!isTrayIconPopup) {
134            ::MapWindowPoints(awtOrigin->GetHWnd(), 0, (LPPOINT)&pt, 1);
135
136            // Adjust to account for the Inset values
137            RECT rctInsets;
138            awtOrigin->GetInsets(&rctInsets);
139            pt.x -= rctInsets.left;
140            pt.y -= rctInsets.top;
141
142            flags = TPM_LEFTALIGN | TPM_RIGHTBUTTON;
143
144        } else {
145            ::SetForegroundWindow(awtOrigin->GetHWnd());
146
147            flags = TPM_NONOTIFY | TPM_RIGHTALIGN | TPM_RIGHTBUTTON | TPM_BOTTOMALIGN;
148        }
149
150        /* Invoke the popup. */
151        ::TrackPopupMenu(GetHMenu(), flags, pt.x, pt.y, 0, awtOrigin->GetHWnd(), NULL);
152
153        if (isTrayIconPopup) {
154            ::PostMessage(awtOrigin->GetHWnd(), WM_NULL, 0, 0);
155        }
156    }
157 done:
158    env->DeleteLocalRef(origin);
159    env->DeleteLocalRef(peerOrigin);
160}
161
162void AwtPopupMenu::_Show(void *param)
163{
164    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
165
166    static jclass popupMenuCls;
167    if (popupMenuCls == NULL) {
168        jclass popupMenuClsLocal = env->FindClass("java/awt/PopupMenu");
169        if (popupMenuClsLocal != NULL) {
170            popupMenuCls = (jclass)env->NewGlobalRef(popupMenuClsLocal);
171            env->DeleteLocalRef(popupMenuClsLocal);
172        }
173    }
174
175    static jfieldID isTrayIconPopupID;
176    if (popupMenuCls != NULL && isTrayIconPopupID == NULL) {
177        isTrayIconPopupID = env->GetFieldID(popupMenuCls, "isTrayIconPopup", "Z");
178        DASSERT(isTrayIconPopupID);
179    }
180
181    ShowStruct *ss = (ShowStruct*)param;
182    if (ss->self != NULL && isTrayIconPopupID != NULL) {
183        PDATA pData = JNI_GET_PDATA(ss->self);
184        if (pData) {
185            AwtPopupMenu *p = (AwtPopupMenu *)pData;
186            jobject target = p->GetTarget(env);
187            BOOL isTrayIconPopup = env->GetBooleanField(target, isTrayIconPopupID);
188            env->DeleteLocalRef(target);
189            p->Show(env, ss->event, isTrayIconPopup);
190        }
191    }
192    if (ss->self != NULL) {
193        env->DeleteGlobalRef(ss->self);
194    }
195    if (ss->event != NULL) {
196        env->DeleteGlobalRef(ss->event);
197    }
198    delete ss;
199    if (isTrayIconPopupID == NULL) {
200        throw std::bad_alloc();
201    }
202}
203
204void AwtPopupMenu::AddItem(AwtMenuItem *item)
205{
206    AwtMenu::AddItem(item);
207    if (GetMenuContainer() != NULL) return;
208    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
209    if (env->EnsureLocalCapacity(1) < 0) {
210        return;
211    }
212    jobject target = GetTarget(env);
213    if (!(jboolean)env->GetBooleanField(target, AwtMenuItem::enabledID)) {
214        item->Enable(FALSE);
215    }
216    env->DeleteLocalRef(target);
217}
218
219void AwtPopupMenu::Enable(BOOL isEnabled)
220{
221    AwtMenu *menu = GetMenuContainer();
222    if (menu != NULL) {
223        AwtMenu::Enable(isEnabled);
224        return;
225    }
226    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
227    if (env->EnsureLocalCapacity(1) < 0) {
228        return;
229    }
230    jobject target = GetTarget(env);
231    int nCount = CountItem(target);
232    for (int i = 0; i < nCount; ++i) {
233        AwtMenuItem *item = GetItem(target,i);
234        jobject jitem = item->GetTarget(env);
235        BOOL bItemEnabled = isEnabled && (jboolean)env->GetBooleanField(jitem,
236            AwtMenuItem::enabledID);
237        jstring labelStr = static_cast<jstring>(env->GetObjectField(jitem, AwtMenuItem::labelID));
238        LPCWSTR labelStrW = JNU_GetStringPlatformChars(env, labelStr, NULL);
239        if (labelStrW  && wcscmp(labelStrW, L"-") != 0) {
240            item->Enable(bItemEnabled);
241        }
242        JNU_ReleaseStringPlatformChars(env, labelStr, labelStrW);
243        env->DeleteLocalRef(labelStr);
244        env->DeleteLocalRef(jitem);
245    }
246    env->DeleteLocalRef(target);
247}
248
249BOOL AwtPopupMenu::IsDisabledAndPopup()
250{
251    if (GetMenuContainer() != NULL) return FALSE;
252    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
253    if (env->EnsureLocalCapacity(1) < 0) {
254        return FALSE;
255    }
256    jobject target = GetTarget(env);
257    BOOL bEnabled = (jboolean)env->GetBooleanField(target,
258            AwtMenuItem::enabledID);
259    env->DeleteLocalRef(target);
260    return !bEnabled;
261}
262
263/************************************************************************
264 * WPopupMenuPeer native methods
265 */
266
267extern "C" {
268
269/*
270 * Class:     sun_awt_windows_WPopupMenuPeer
271 * Method:    createMenu
272 * Signature: (Lsun/awt/windows/WComponentPeer;)V
273 */
274JNIEXPORT void JNICALL
275Java_sun_awt_windows_WPopupMenuPeer_createMenu(JNIEnv *env, jobject self,
276                                               jobject parent)
277{
278    TRY;
279
280    AwtToolkit::CreateComponent(
281        self, parent, (AwtToolkit::ComponentFactory)AwtPopupMenu::Create);
282
283    CATCH_BAD_ALLOC;
284}
285
286/*
287 * Class:     sun_awt_windows_WPopupMenuPeer
288 * Method:    _show
289 * Signature: (Ljava/awt/Event;)V
290 */
291JNIEXPORT void JNICALL
292Java_sun_awt_windows_WPopupMenuPeer__1show(JNIEnv *env, jobject self,
293                                           jobject event)
294{
295    TRY;
296
297    ShowStruct *ss = new ShowStruct;
298    ss->self = env->NewGlobalRef(self);
299    ss->event = env->NewGlobalRef(event);
300
301    // fix for 6268046: invoke the function without CriticalSection's synchronization
302    AwtToolkit::GetInstance().InvokeFunction(AwtPopupMenu::_Show, ss);
303    // global ref and ss are deleted in _Show()
304
305    CATCH_BAD_ALLOC;
306}
307
308} /* extern "C" */
309