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