1 /*
   2  * Copyright (c) 1996, 2007, 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 <java_awt_PopupMenu.h>
  32 #include <sun_awt_windows_WPopupMenuPeer.h>
  33 #include <java_awt_Event.h>
  34 
  35 /* IMPORTANT! Read the README.JNI file for notes on JNI converted AWT code.
  36  */
  37 
  38 /***********************************************************************/
  39 // struct for _Show method
  40 struct ShowStruct {
  41     jobject self;
  42     jobject event;
  43 };
  44 
  45 /************************************************************************
  46  * AwtPopupMenu class methods
  47  */
  48 
  49 AwtPopupMenu::AwtPopupMenu() {
  50     m_parent = NULL;
  51 }
  52 
  53 AwtPopupMenu::~AwtPopupMenu()
  54 {
  55 }
  56 
  57 void AwtPopupMenu::Dispose()
  58 {
  59     m_parent = NULL;
  60 
  61     AwtMenu::Dispose();
  62 }
  63 
  64 LPCTSTR AwtPopupMenu::GetClassName() {
  65   return TEXT("SunAwtPopupMenu");
  66 }
  67 
  68 /* Create a new AwtPopupMenu object and menu.   */
  69 AwtPopupMenu* AwtPopupMenu::Create(jobject self, AwtComponent* parent)
  70 {
  71     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
  72 
  73     jobject target = NULL;
  74     AwtPopupMenu* popupMenu = NULL;
  75 
  76     try {
  77         if (env->EnsureLocalCapacity(1) < 0) {
  78             return NULL;
  79         }
  80 
  81         target = env->GetObjectField(self, AwtObject::targetID);
  82         JNI_CHECK_NULL_GOTO(target, "null target", done);
  83 
  84         popupMenu = new AwtPopupMenu();
  85 
  86         SetLastError(0);
  87         HMENU hMenu = ::CreatePopupMenu();
  88         // fix for 5088782
  89         if (!CheckMenuCreation(env, self, hMenu))
  90         {
  91             env->DeleteLocalRef(target);
  92             return NULL;
  93         }
  94 
  95         popupMenu->SetHMenu(hMenu);
  96 
  97         popupMenu->LinkObjects(env, self);
  98         popupMenu->SetParent(parent);
  99     } catch (...) {
 100         env->DeleteLocalRef(target);
 101         throw;
 102     }
 103 
 104 done:
 105     env->DeleteLocalRef(target);
 106     return popupMenu;
 107 }
 108 
 109 void AwtPopupMenu::Show(JNIEnv *env, jobject event, BOOL isTrayIconPopup)
 110 {
 111     /*
 112      * For not TrayIcon popup.
 113      * Convert the event's XY to absolute coordinates.  The XY is
 114      * relative to the origin component, which is passed by PopupMenu
 115      * as the event's target.
 116      */
 117     if (env->EnsureLocalCapacity(2) < 0) {
 118         env->DeleteGlobalRef(event);
 119         return;
 120     }
 121     jobject origin = (env)->GetObjectField(event, AwtEvent::targetID);
 122     jobject peerOrigin = GetPeerForTarget(env, origin);
 123     PDATA pData;
 124     JNI_CHECK_PEER_GOTO(peerOrigin, done);
 125     {
 126         AwtComponent* awtOrigin = (AwtComponent*)pData;
 127         POINT pt;
 128         UINT flags = 0;
 129         pt.x = (env)->GetIntField(event, AwtEvent::xID);
 130         pt.y = (env)->GetIntField(event, AwtEvent::yID);
 131 
 132         if (!isTrayIconPopup) {
 133             ::MapWindowPoints(awtOrigin->GetHWnd(), 0, (LPPOINT)&pt, 1);
 134 
 135             // Adjust to account for the Inset values
 136             RECT rctInsets;
 137             awtOrigin->GetInsets(&rctInsets);
 138             pt.x -= rctInsets.left;
 139             pt.y -= rctInsets.top;
 140 
 141             flags = TPM_LEFTALIGN | TPM_RIGHTBUTTON;
 142 
 143         } else {
 144             ::SetForegroundWindow(awtOrigin->GetHWnd());
 145 
 146             flags = TPM_NONOTIFY | TPM_RIGHTALIGN | TPM_RIGHTBUTTON | TPM_BOTTOMALIGN;
 147         }
 148 
 149         /* Invoke the popup. */
 150         ::TrackPopupMenu(GetHMenu(), flags, pt.x, pt.y, 0, awtOrigin->GetHWnd(), NULL);
 151 
 152         if (isTrayIconPopup) {
 153             ::PostMessage(awtOrigin->GetHWnd(), WM_NULL, 0, 0);
 154         }
 155     }
 156  done:
 157     env->DeleteLocalRef(origin);
 158     env->DeleteLocalRef(peerOrigin);
 159     env->DeleteGlobalRef(event);
 160 }
 161 
 162 void 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 =
 169             env->FindClass("java/awt/PopupMenu");
 170         if (!popupMenuClsLocal) {
 171             /* exception already thrown */
 172             ShowStruct *ss = (ShowStruct*)param;
 173             if (ss->self != NULL) {
 174                 env->DeleteGlobalRef(ss->self);
 175             }
 176             delete ss;
 177             return;
 178         }
 179         popupMenuCls = (jclass)env->NewGlobalRef(popupMenuClsLocal);
 180         env->DeleteLocalRef(popupMenuClsLocal);
 181     }
 182 
 183     static jfieldID isTrayIconPopupID;
 184     if (isTrayIconPopupID == NULL) {
 185         isTrayIconPopupID = env->GetFieldID(popupMenuCls, "isTrayIconPopup", "Z");
 186         DASSERT(isTrayIconPopupID);
 187     }
 188 
 189     ShowStruct *ss = (ShowStruct*)param;
 190     if (ss->self != NULL) {
 191         PDATA pData = JNI_GET_PDATA(ss->self);
 192         if (pData) {
 193             AwtPopupMenu *p = (AwtPopupMenu *)pData;
 194             jobject target = p->GetTarget(env);
 195             BOOL isTrayIconPopup = env->GetBooleanField(target, isTrayIconPopupID);
 196             env->DeleteLocalRef(target);
 197             p->Show(env, ss->event, isTrayIconPopup);
 198         }
 199         env->DeleteGlobalRef(ss->self);
 200     }
 201     delete ss;
 202 }
 203 
 204 void 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 
 219 void 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 
 249 BOOL 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 
 267 extern "C" {
 268 
 269 /*
 270  * Class:     sun_awt_windows_WPopupMenuPeer
 271  * Method:    createMenu
 272  * Signature: (Lsun/awt/windows/WComponentPeer;)V
 273  */
 274 JNIEXPORT void JNICALL
 275 Java_sun_awt_windows_WPopupMenuPeer_createMenu(JNIEnv *env, jobject self,
 276                                                jobject parent)
 277 {
 278     TRY;
 279 
 280     PDATA pData;
 281     JNI_CHECK_PEER_RETURN(parent);
 282     AwtComponent* awtParent = (AwtComponent *)pData;
 283     AwtToolkit::CreateComponent(
 284         self, awtParent, (AwtToolkit::ComponentFactory)AwtPopupMenu::Create, FALSE);
 285     JNI_CHECK_PEER_CREATION_RETURN(self);
 286 
 287     CATCH_BAD_ALLOC;
 288 }
 289 
 290 /*
 291  * Class:     sun_awt_windows_WPopupMenuPeer
 292  * Method:    _show
 293  * Signature: (Ljava/awt/Event;)V
 294  */
 295 JNIEXPORT void JNICALL
 296 Java_sun_awt_windows_WPopupMenuPeer__1show(JNIEnv *env, jobject self,
 297                                            jobject event)
 298 {
 299     TRY;
 300 
 301     ShowStruct *ss = new ShowStruct;
 302     ss->self = env->NewGlobalRef(self);
 303     ss->event = env->NewGlobalRef(event);
 304 
 305     // fix for 6268046: invoke the function without CriticalSection's synchronization
 306     AwtToolkit::GetInstance().InvokeFunction(AwtPopupMenu::_Show, ss);
 307     // global ref is deleted in _Show() and ss is deleted in Show()
 308 
 309     CATCH_BAD_ALLOC;
 310 }
 311 
 312 } /* extern "C" */