1 /*
   2  * Copyright (c) 2005, 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.h"
  27 #include <windowsx.h>
  28 #include <shellapi.h>
  29 #include <shlwapi.h>
  30 
  31 #include "awt_Toolkit.h"
  32 #include "awt_TrayIcon.h"
  33 #include "awt_AWTEvent.h"
  34 
  35 #include <java_awt_event_InputEvent.h>
  36 
  37 /***********************************************************************/
  38 // Struct for _SetToolTip() method
  39 struct SetToolTipStruct {
  40     jobject trayIcon;
  41     jstring tooltip;
  42 };
  43 // Struct for _SetIcon() method
  44 struct SetIconStruct {
  45     jobject trayIcon;
  46     HICON hIcon;
  47 };
  48 // Struct for _UpdateIcon() method
  49 struct UpdateIconStruct {
  50     jobject trayIcon;
  51     jboolean update;
  52 };
  53 // Struct for _DisplayMessage() method
  54 struct DisplayMessageStruct {
  55     jobject trayIcon;
  56     jstring caption;
  57     jstring text;
  58     jstring msgType;
  59 };
  60 
  61 typedef struct tagBitmapheader  {
  62     BITMAPV5HEADER bmiHeader;
  63     DWORD            dwMasks[256];
  64 } Bitmapheader, *LPBITMAPHEADER;
  65 
  66 
  67 /************************************************************************
  68  * AwtTrayIcon fields
  69  */
  70 
  71 jfieldID AwtTrayIcon::idID;
  72 jfieldID AwtTrayIcon::actionCommandID;
  73 
  74 HWND AwtTrayIcon::sm_msgWindow = NULL;
  75 AwtTrayIcon::TrayIconListItem* AwtTrayIcon::sm_trayIconList = NULL;
  76 int AwtTrayIcon::sm_instCount = 0;
  77 
  78 /************************************************************************
  79  * AwtTrayIcon methods
  80  */
  81 
  82 AwtTrayIcon::AwtTrayIcon() {
  83     ::ZeroMemory(&m_nid, sizeof(m_nid));
  84 
  85     if (sm_instCount++ == 0 && AwtTrayIcon::sm_msgWindow == NULL) {
  86         sm_msgWindow = AwtTrayIcon::CreateMessageWindow();
  87     }
  88     m_mouseButtonClickAllowed = 0;
  89 }
  90 
  91 AwtTrayIcon::~AwtTrayIcon() {
  92 }
  93 
  94 void AwtTrayIcon::Dispose() {
  95     SendTrayMessage(NIM_DELETE);
  96 
  97     // Destroy the icon to avoid leak of GDI objects
  98     if (m_nid.hIcon != NULL) {
  99         ::DestroyIcon(m_nid.hIcon);
 100     }
 101 
 102     UnlinkObjects();
 103 
 104     if (--sm_instCount == 0) {
 105         AwtTrayIcon::DestroyMessageWindow();
 106     }
 107 
 108     AwtObject::Dispose();
 109 }
 110 
 111 LPCTSTR AwtTrayIcon::GetClassName() {
 112     return TEXT("SunAwtTrayIcon");
 113 }
 114 
 115 void AwtTrayIcon::FillClassInfo(WNDCLASS *lpwc)
 116 {
 117     lpwc->style         = 0L;
 118     lpwc->lpfnWndProc   = (WNDPROC)TrayWindowProc;
 119     lpwc->cbClsExtra    = 0;
 120     lpwc->cbWndExtra    = 0;
 121     lpwc->hInstance     = AwtToolkit::GetInstance().GetModuleHandle(),
 122     lpwc->hIcon         = AwtToolkit::GetInstance().GetAwtIcon();
 123     lpwc->hCursor       = NULL;
 124     lpwc->hbrBackground = NULL;
 125     lpwc->lpszMenuName  = NULL;
 126     lpwc->lpszClassName = AwtTrayIcon::GetClassName();
 127 }
 128 
 129 void AwtTrayIcon::RegisterClass()
 130 {
 131     WNDCLASS  wc;
 132 
 133     ::ZeroMemory(&wc, sizeof(wc));
 134 
 135     if (!::GetClassInfo(AwtToolkit::GetInstance().GetModuleHandle(),
 136                         AwtTrayIcon::GetClassName(), &wc))
 137     {
 138         AwtTrayIcon::FillClassInfo(&wc);
 139         ATOM atom = ::RegisterClass(&wc);
 140         DASSERT(atom != 0);
 141     }
 142 }
 143 
 144 void AwtTrayIcon::UnregisterClass()
 145 {
 146     ::UnregisterClass(AwtTrayIcon::GetClassName(), AwtToolkit::GetInstance().GetModuleHandle());
 147 }
 148 
 149 HWND AwtTrayIcon::CreateMessageWindow()
 150 {
 151     AwtTrayIcon::RegisterClass();
 152 
 153     HWND hWnd = ::CreateWindow(AwtTrayIcon::GetClassName(), TEXT("TrayMessageWindow"),
 154                                0, 0, 0, 0, 0, NULL, NULL,
 155                                AwtToolkit::GetInstance().GetModuleHandle(), NULL);
 156     return hWnd;
 157 }
 158 
 159 void AwtTrayIcon::DestroyMessageWindow()
 160 {
 161     ::DestroyWindow(AwtTrayIcon::sm_msgWindow);
 162     AwtTrayIcon::sm_msgWindow = NULL;
 163     AwtTrayIcon::UnregisterClass();
 164 }
 165 
 166 AwtTrayIcon* AwtTrayIcon::Create(jobject self, jobject parent)
 167 {
 168     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 169     jobject target = NULL;
 170     AwtTrayIcon* awtTrayIcon = NULL;
 171 
 172     target  = env->GetObjectField(self, AwtObject::targetID);
 173     DASSERT(target);
 174 
 175     awtTrayIcon = new AwtTrayIcon();
 176     awtTrayIcon->LinkObjects(env, self);
 177     awtTrayIcon->InitNID(env->GetIntField(target, AwtTrayIcon::idID));
 178     awtTrayIcon->AddTrayIconItem(awtTrayIcon->GetID());
 179 
 180     env->DeleteLocalRef(target);
 181     return awtTrayIcon;
 182 }
 183 
 184 void AwtTrayIcon::InitNID(UINT uID)
 185 {
 186     // fix for 6271589: we MUST set the size of the structure to match
 187     // the shell version, otherwise some errors may occur (like missing
 188     // balloon messages on win2k)
 189     DLLVERSIONINFO dllVersionInfo;
 190     dllVersionInfo.cbSize = sizeof(DLLVERSIONINFO);
 191     int shellVersion = 5; // WIN_2000
 192     // MSDN: DllGetVersion should not be implicitly called, but rather
 193     // loaded using GetProcAddress
 194     HMODULE hShell = JDK_LoadSystemLibrary("Shell32.dll");
 195     if (hShell != NULL) {
 196         DLLGETVERSIONPROC proc = (DLLGETVERSIONPROC)GetProcAddress(hShell, "DllGetVersion");
 197         if (proc != NULL) {
 198             if (proc(&dllVersionInfo) == NOERROR) {
 199                 shellVersion = dllVersionInfo.dwMajorVersion;
 200             }
 201         }
 202     }
 203     FreeLibrary(hShell);
 204     switch (shellVersion) {
 205         case 5: // WIN_2000
 206             m_nid.cbSize = (BYTE *)(&m_nid.guidItem) - (BYTE *)(&m_nid.cbSize);
 207             break;
 208         case 6: // WIN_XP
 209             m_nid.cbSize = (BYTE *)(&m_nid.hBalloonIcon) - (BYTE *)(&m_nid.cbSize);
 210             break;
 211         default: // WIN_VISTA
 212             m_nid.cbSize = sizeof(m_nid);
 213             break;
 214     }
 215     m_nid.hWnd = AwtTrayIcon::sm_msgWindow;
 216     m_nid.uID = uID;
 217     m_nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
 218     m_nid.uCallbackMessage = WM_AWT_TRAY_NOTIFY;
 219     m_nid.hIcon = AwtToolkit::GetInstance().GetAwtIcon();
 220     m_nid.szTip[0] = '\0';
 221     m_nid.uVersion = NOTIFYICON_VERSION;
 222 }
 223 
 224 BOOL AwtTrayIcon::SendTrayMessage(DWORD dwMessage)
 225 {
 226     return Shell_NotifyIcon(dwMessage, (PNOTIFYICONDATA)&m_nid);
 227 }
 228 
 229 static UINT lastMessage = WM_NULL;
 230 
 231 LRESULT CALLBACK AwtTrayIcon::TrayWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 232 {
 233     LRESULT retValue = 0;
 234     MsgRouting mr = mrDoDefault;
 235     static UINT s_msgTaskbarCreated;
 236 
 237     switch(uMsg)
 238     {
 239         case WM_CREATE:
 240             // Fix for CR#6369062
 241             s_msgTaskbarCreated = ::RegisterWindowMessage(TEXT("TaskbarCreated"));
 242             break;
 243         case WM_AWT_TRAY_NOTIFY:
 244             if (hwnd == AwtTrayIcon::sm_msgWindow) {
 245                 AwtTrayIcon* trayIcon = AwtTrayIcon::SearchTrayIconItem((UINT)wParam);
 246                 if (trayIcon != NULL) {
 247                     mr = trayIcon->WmAwtTrayNotify(wParam, lParam);
 248                 }
 249             }
 250             break;
 251         default:
 252             if(uMsg == s_msgTaskbarCreated) {
 253                 if (hwnd == AwtTrayIcon::sm_msgWindow) {
 254                     mr = WmTaskbarCreated();
 255                 }
 256             }
 257             break;
 258     }
 259 
 260     if (mr != mrConsume) {
 261         retValue = ::DefWindowProc(hwnd, uMsg, wParam, lParam);
 262     }
 263     return retValue;
 264 }
 265 
 266 /*
 267  * This function processes callback messages for taskbar icons.
 268  */
 269 MsgRouting AwtTrayIcon::WmAwtTrayNotify(WPARAM wParam, LPARAM lParam)
 270 {
 271     MsgRouting mr = mrDoDefault;
 272 
 273     POINT pos = {0, 0};
 274     ::GetCursorPos(&pos);
 275 
 276     lastMessage = (UINT)lParam;
 277     UINT flags = AwtToolkit::GetInstance().GetMouseKeyState();
 278 
 279     switch((UINT)lParam)
 280     {
 281         case WM_MOUSEMOVE:
 282             mr = WmMouseMove(flags, pos.x, pos.y);
 283             break;
 284         case WM_LBUTTONDBLCLK:
 285         case WM_LBUTTONDOWN:
 286             mr = WmMouseDown(flags, pos.x, pos.y, LEFT_BUTTON);
 287             break;
 288         case WM_LBUTTONUP:
 289             mr = WmMouseUp(flags, pos.x, pos.y, LEFT_BUTTON);
 290             break;
 291         case WM_RBUTTONDBLCLK:
 292         case WM_RBUTTONDOWN:
 293             mr = WmMouseDown(flags, pos.x, pos.y, RIGHT_BUTTON);
 294             break;
 295         case WM_RBUTTONUP:
 296             mr = WmMouseUp(flags, pos.x, pos.y, RIGHT_BUTTON);
 297             break;
 298         case WM_MBUTTONDBLCLK:
 299         case WM_MBUTTONDOWN:
 300             mr = WmMouseDown(flags, pos.x, pos.y, MIDDLE_BUTTON);
 301             break;
 302         case WM_MBUTTONUP:
 303             mr = WmMouseUp(flags, pos.x, pos.y, MIDDLE_BUTTON);
 304             break;
 305         case WM_CONTEXTMENU:
 306             mr = WmContextMenu(0, pos.x, pos.y);
 307             break;
 308         case NIN_KEYSELECT:
 309             mr = WmKeySelect(0, pos.x, pos.y);
 310             break;
 311         case NIN_SELECT:
 312             mr = WmSelect(0, pos.x, pos.y);
 313             break;
 314         case NIN_BALLOONUSERCLICK:
 315             mr = WmBalloonUserClick(0, pos.x, pos.y);
 316             break;
 317     }
 318     return mr;
 319 }
 320 
 321 /* Double-click variables. */
 322 static jlong multiClickTime = ::GetDoubleClickTime();
 323 static int multiClickMaxX = ::GetSystemMetrics(SM_CXDOUBLECLK);
 324 static int multiClickMaxY = ::GetSystemMetrics(SM_CYDOUBLECLK);
 325 static AwtTrayIcon* lastClickTrIc = NULL;
 326 static jlong lastTime = 0;
 327 static int lastClickX = 0;
 328 static int lastClickY = 0;
 329 static int lastButton = 0;
 330 static int clickCount = 0;
 331 
 332 MsgRouting AwtTrayIcon::WmMouseDown(UINT flags, int x, int y, int button)
 333 {
 334     jlong now = ::JVM_CurrentTimeMillis(NULL, 0);
 335     jint javaModif = AwtComponent::GetJavaModifiers();
 336 
 337     if (lastClickTrIc == this &&
 338         lastButton == button &&
 339         (now - lastTime) <= multiClickTime &&
 340         abs(x - lastClickX) <= multiClickMaxX &&
 341         abs(y - lastClickY) <= multiClickMaxY)
 342     {
 343         clickCount++;
 344     } else {
 345         clickCount = 1;
 346         lastClickTrIc = this;
 347         lastButton = button;
 348         lastClickX = x;
 349         lastClickY = y;
 350     }
 351     lastTime = now;
 352     // it's needed only if WM_LBUTTONUP doesn't come for some reason
 353     m_mouseButtonClickAllowed |= AwtComponent::GetButtonMK(button);
 354 
 355     MSG msg;
 356     AwtComponent::InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y);
 357 
 358     SendMouseEvent(java_awt_event_MouseEvent_MOUSE_PRESSED, now, x, y,
 359                    javaModif, clickCount, JNI_FALSE,
 360                    AwtComponent::GetButton(button), &msg);
 361 
 362     return mrConsume;
 363 }
 364 
 365 MsgRouting AwtTrayIcon::WmMouseUp(UINT flags, int x, int y, int button)
 366 {
 367     MSG msg;
 368     AwtComponent::InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y);
 369 
 370     SendMouseEvent(java_awt_event_MouseEvent_MOUSE_RELEASED, ::JVM_CurrentTimeMillis(NULL, 0),
 371                    x, y, AwtComponent::GetJavaModifiers(), clickCount,
 372                    (AwtComponent::GetButton(button) == java_awt_event_MouseEvent_BUTTON3 ?
 373                     TRUE : FALSE), AwtComponent::GetButton(button), &msg);
 374 
 375     if ((m_mouseButtonClickAllowed & AwtComponent::GetButtonMK(button)) != 0) { // No up-button in the drag-state
 376         SendMouseEvent(java_awt_event_MouseEvent_MOUSE_CLICKED,
 377                        ::JVM_CurrentTimeMillis(NULL, 0), x, y, AwtComponent::GetJavaModifiers(),
 378                        clickCount, JNI_FALSE, AwtComponent::GetButton(button));
 379     }
 380     m_mouseButtonClickAllowed &= ~AwtComponent::GetButtonMK(button); // Exclude the up-button from the drag-state
 381 
 382     return mrConsume;
 383 }
 384 
 385 MsgRouting AwtTrayIcon::WmMouseMove(UINT flags, int x, int y)
 386 {
 387     MSG msg;
 388     static AwtTrayIcon* lastComp = NULL;
 389     static int lastX = 0;
 390     static int lastY = 0;
 391 
 392     /*
 393      * Workaround for CR#6267980
 394      * Windows sends WM_MOUSEMOVE if mouse is motionless
 395      */
 396     if (lastComp != this || x != lastX || y != lastY) {
 397         lastComp = this;
 398         lastX = x;
 399         lastY = y;
 400         AwtComponent::InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y);
 401         if ((flags & ALL_MK_BUTTONS) != 0) {
 402             m_mouseButtonClickAllowed = 0;
 403         } else {
 404             SendMouseEvent(java_awt_event_MouseEvent_MOUSE_MOVED, ::JVM_CurrentTimeMillis(NULL, 0), x, y,
 405                            AwtComponent::GetJavaModifiers(), 0, JNI_FALSE,
 406                            java_awt_event_MouseEvent_NOBUTTON, &msg);
 407         }
 408     }
 409     return mrConsume;
 410 }
 411 
 412 MsgRouting AwtTrayIcon::WmBalloonUserClick(UINT flags, int x, int y)
 413 {
 414     // The windows api GetKeyState() when read would provide the key state of the requrested key
 415     // but it is not guaranteed to receive the same as it is stored in the thread message queue and
 416     // unless the thread runs faster.
 417     // Event NIN_BALLOONUSERCLICK is received only upon left mouse click. Hence the additional check
 418     // is not required.
 419     MSG msg;
 420     AwtComponent::InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y);
 421     SendActionEvent(java_awt_event_ActionEvent_ACTION_PERFORMED, ::JVM_CurrentTimeMillis(NULL, 0),
 422                     AwtComponent::GetActionModifiers(), &msg);
 423     return mrConsume;
 424 }
 425 
 426 MsgRouting AwtTrayIcon::WmKeySelect(UINT flags, int x, int y)
 427 {
 428     static jlong lastKeySelectTime = 0;
 429     jlong now = ::JVM_CurrentTimeMillis(NULL, 0);
 430 
 431     // If a user selects a notify icon with the ENTER key,
 432     // Shell 5.0 sends double NIN_KEYSELECT notification.
 433     if (lastKeySelectTime != now) {
 434         MSG msg;
 435         AwtComponent::InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y);
 436         SendActionEvent(java_awt_event_ActionEvent_ACTION_PERFORMED, ::JVM_CurrentTimeMillis(NULL, 0),
 437                         AwtComponent::GetActionModifiers(), &msg);
 438     }
 439     lastKeySelectTime = now;
 440 
 441     return mrConsume;
 442 }
 443 
 444 MsgRouting AwtTrayIcon::WmSelect(UINT flags, int x, int y)
 445 {
 446 
 447     // If a user click on a notify icon with the mouse,
 448     // Shell 5.0 sends NIN_SELECT notification on every click.
 449     // To be compatible with JDK6.0 only second click is important.
 450     if (clickCount == 2) {
 451         MSG msg;
 452         AwtComponent::InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y);
 453         SendActionEvent(java_awt_event_ActionEvent_ACTION_PERFORMED, ::JVM_CurrentTimeMillis(NULL, 0),
 454                         AwtComponent::GetActionModifiers(), &msg);
 455     }
 456     return mrConsume;
 457 }
 458 
 459 MsgRouting AwtTrayIcon::WmContextMenu(UINT flags, int x, int y)
 460 {
 461     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 462     jobject peer = GetPeer(env);
 463     if (peer != NULL) {
 464         JNU_CallMethodByName(env, NULL, peer, "showPopupMenu",
 465                              "(II)V", x, y);
 466     }
 467     return mrConsume;
 468 }
 469 
 470 /*
 471  * Adds all icons we already have to taskbar.
 472  * We use this method on taskbar recreation (see 6369062).
 473  */
 474 MsgRouting AwtTrayIcon::WmTaskbarCreated() {
 475     TrayIconListItem* item;
 476     for (item = sm_trayIconList; item != NULL; item = item->m_next) {
 477         BOOL result = item->m_trayIcon->SendTrayMessage(NIM_ADD);
 478         // 6270114: Instructs the taskbar to behave according to the Shell version 5.0
 479         if (result) {
 480             item->m_trayIcon->SendTrayMessage(NIM_SETVERSION);
 481         }
 482     }
 483     return mrDoDefault;
 484 }
 485 
 486 void AwtTrayIcon::SendMouseEvent(jint id, jlong when, jint x, jint y,
 487                                  jint modifiers, jint clickCount,
 488                                  jboolean popupTrigger, jint button,
 489                                  MSG *pMsg)
 490 {
 491     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 492     if (GetPeer(env) == NULL) {
 493         /* event received during termination. */
 494         return;
 495     }
 496 
 497     static jclass mouseEventCls;
 498     if (mouseEventCls == NULL) {
 499         jclass mouseEventClsLocal =
 500             env->FindClass("java/awt/event/MouseEvent");
 501         if (!mouseEventClsLocal) {
 502             /* exception already thrown */
 503             return;
 504         }
 505         mouseEventCls = (jclass)env->NewGlobalRef(mouseEventClsLocal);
 506         env->DeleteLocalRef(mouseEventClsLocal);
 507     }
 508 
 509     static jmethodID mouseEventConst;
 510     if (mouseEventConst == NULL) {
 511         mouseEventConst =
 512             env->GetMethodID(mouseEventCls, "<init>",
 513                              "(Ljava/awt/Component;IJIIIIIIZI)V");
 514         DASSERT(mouseEventConst);
 515         CHECK_NULL(mouseEventConst);
 516     }
 517     if (env->EnsureLocalCapacity(2) < 0) {
 518         return;
 519     }
 520     jobject target = GetTarget(env);
 521     jobject mouseEvent = env->NewObject(mouseEventCls, mouseEventConst,
 522                                         target,
 523                                         id, when, modifiers,
 524                                         x, y, // no client area coordinates
 525                                         x, y,
 526                                         clickCount, popupTrigger, button);
 527 
 528     if (safe_ExceptionOccurred(env)) {
 529         env->ExceptionDescribe();
 530         env->ExceptionClear();
 531     }
 532 
 533     DASSERT(mouseEvent != NULL);
 534     if (pMsg != 0) {
 535         AwtAWTEvent::saveMSG(env, pMsg, mouseEvent);
 536     }
 537     SendEvent(mouseEvent);
 538 
 539     env->DeleteLocalRef(mouseEvent);
 540     env->DeleteLocalRef(target);
 541 }
 542 
 543 void AwtTrayIcon::SendActionEvent(jint id, jlong when, jint modifiers, MSG *pMsg)
 544 {
 545     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 546     if (GetPeer(env) == NULL) {
 547         /* event received during termination. */
 548         return;
 549     }
 550 
 551     static jclass actionEventCls;
 552     if (actionEventCls == NULL) {
 553         jclass actionEventClsLocal =
 554             env->FindClass("java/awt/event/ActionEvent");
 555         if (!actionEventClsLocal) {
 556             /* exception already thrown */
 557             return;
 558         }
 559         actionEventCls = (jclass)env->NewGlobalRef(actionEventClsLocal);
 560         env->DeleteLocalRef(actionEventClsLocal);
 561     }
 562 
 563     static jmethodID actionEventConst;
 564     if (actionEventConst == NULL) {
 565         actionEventConst =
 566             env->GetMethodID(actionEventCls, "<init>",
 567                              "(Ljava/lang/Object;ILjava/lang/String;JI)V");
 568         DASSERT(actionEventConst);
 569         CHECK_NULL(actionEventConst);
 570     }
 571     if (env->EnsureLocalCapacity(2) < 0) {
 572         return;
 573     }
 574     jobject target = GetTarget(env);
 575     jstring actionCommand = (jstring)env->GetObjectField(target, AwtTrayIcon::actionCommandID);
 576     jobject actionEvent = env->NewObject(actionEventCls, actionEventConst,
 577                                          target, id, actionCommand, when, modifiers);
 578 
 579     if (safe_ExceptionOccurred(env)) {
 580         env->ExceptionDescribe();
 581         env->ExceptionClear();
 582     }
 583 
 584     DASSERT(actionEvent != NULL);
 585     if (pMsg != 0) {
 586         AwtAWTEvent::saveMSG(env, pMsg, actionEvent);
 587     }
 588     SendEvent(actionEvent);
 589 
 590     env->DeleteLocalRef(actionEvent);
 591     env->DeleteLocalRef(target);
 592     env->DeleteLocalRef(actionCommand);
 593 }
 594 
 595 AwtTrayIcon* AwtTrayIcon::SearchTrayIconItem(UINT id) {
 596     TrayIconListItem* item;
 597     for (item = sm_trayIconList; item != NULL; item = item->m_next) {
 598         if (item->m_ID == id) {
 599             return item->m_trayIcon;
 600         }
 601     }
 602     /*
 603      * DASSERT(FALSE);
 604      * This should not be happend if all tray icons are recorded
 605      */
 606     return NULL;
 607 }
 608 
 609 void AwtTrayIcon::RemoveTrayIconItem(UINT id) {
 610     TrayIconListItem* item = sm_trayIconList;
 611     TrayIconListItem* lastItem = NULL;
 612     while (item != NULL) {
 613         if (item->m_ID == id) {
 614             if (lastItem == NULL) {
 615                 sm_trayIconList = item->m_next;
 616             } else {
 617                 lastItem->m_next = item->m_next;
 618             }
 619             item->m_next = NULL;
 620             DASSERT(item != NULL);
 621             delete item;
 622             return;
 623         }
 624         lastItem = item;
 625         item = item->m_next;
 626     }
 627 }
 628 
 629 void AwtTrayIcon::LinkObjects(JNIEnv *env, jobject peer)
 630 {
 631     if (m_peerObject == NULL) {
 632         m_peerObject = env->NewGlobalRef(peer);
 633     }
 634 
 635     /* Bind JavaPeer -> C++*/
 636     JNI_SET_PDATA(peer, this);
 637 }
 638 
 639 void AwtTrayIcon::UnlinkObjects()
 640 {
 641     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 642     if (m_peerObject) {
 643         JNI_SET_PDATA(m_peerObject, static_cast<PDATA>(NULL));
 644         env->DeleteGlobalRef(m_peerObject);
 645         m_peerObject = NULL;
 646     }
 647 }
 648 
 649 HBITMAP AwtTrayIcon::CreateBMP(HWND hW,int* imageData,int nSS, int nW, int nH)
 650 {
 651     Bitmapheader    bmhHeader = {0};
 652     HDC             hDC;
 653     char            *ptrImageData;
 654     HBITMAP         hbmpBitmap;
 655     HBITMAP         hBitmap;
 656     int             nNumChannels    = 4;
 657 
 658     if (!hW) {
 659         hW = ::GetDesktopWindow();
 660     }
 661     hDC = ::GetDC(hW);
 662     if (!hDC) {
 663         return NULL;
 664     }
 665 
 666     bmhHeader.bmiHeader.bV5Size              = sizeof(BITMAPV5HEADER);
 667     bmhHeader.bmiHeader.bV5Width             = nW;
 668     bmhHeader.bmiHeader.bV5Height            = -nH;
 669     bmhHeader.bmiHeader.bV5Planes            = 1;
 670 
 671     bmhHeader.bmiHeader.bV5BitCount          = 32;
 672     bmhHeader.bmiHeader.bV5Compression       = BI_BITFIELDS;
 673 
 674     // The following mask specification specifies a supported 32 BPP
 675     // alpha format for Windows XP.
 676     bmhHeader.bmiHeader.bV5RedMask   =  0x00FF0000;
 677     bmhHeader.bmiHeader.bV5GreenMask =  0x0000FF00;
 678     bmhHeader.bmiHeader.bV5BlueMask  =  0x000000FF;
 679     bmhHeader.bmiHeader.bV5AlphaMask =  0xFF000000;
 680 
 681     hbmpBitmap = ::CreateDIBSection(hDC, (BITMAPINFO*)&(bmhHeader),
 682                                     DIB_RGB_COLORS,
 683                                     (void**)&(ptrImageData),
 684                                     NULL, 0);
 685     int  *srcPtr = imageData;
 686     char *dstPtr = ptrImageData;
 687     if (!dstPtr) {
 688         ReleaseDC(hW, hDC);
 689         return NULL;
 690     }
 691     for (int nOutern = 0; nOutern < nH; nOutern++) {
 692         for (int nInner = 0; nInner < nSS; nInner++) {
 693             dstPtr[3] = (*srcPtr >> 0x18) & 0xFF;
 694             dstPtr[2] = (*srcPtr >> 0x10) & 0xFF;
 695             dstPtr[1] = (*srcPtr >> 0x08) & 0xFF;
 696             dstPtr[0] = *srcPtr & 0xFF;
 697 
 698             srcPtr++;
 699             dstPtr += nNumChannels;
 700         }
 701     }
 702 
 703     // convert it into DDB to make CustomCursor work on WIN95
 704     hBitmap = CreateDIBitmap(hDC,
 705                              (BITMAPINFOHEADER*)&bmhHeader,
 706                              CBM_INIT,
 707                              (void *)ptrImageData,
 708                              (BITMAPINFO*)&bmhHeader,
 709                              DIB_RGB_COLORS);
 710 
 711     ::DeleteObject(hbmpBitmap);
 712     ::ReleaseDC(hW, hDC);
 713 //  ::GdiFlush();
 714     return hBitmap;
 715 }
 716 
 717 void AwtTrayIcon::SetToolTip(LPCTSTR tooltip)
 718 {
 719     if (tooltip == NULL) {
 720         m_nid.szTip[0] = '\0';
 721     } else if (lstrlen(tooltip) >= TRAY_ICON_TOOLTIP_MAX_SIZE) {
 722         _tcsncpy(m_nid.szTip, tooltip, TRAY_ICON_TOOLTIP_MAX_SIZE);
 723         m_nid.szTip[TRAY_ICON_TOOLTIP_MAX_SIZE - 1] = '\0';
 724     } else {
 725         _tcscpy_s(m_nid.szTip, TRAY_ICON_TOOLTIP_MAX_SIZE, tooltip);
 726     }
 727 
 728     SendTrayMessage(NIM_MODIFY);
 729 }
 730 
 731 void AwtTrayIcon::_SetToolTip(void *param)
 732 {
 733     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 734     SetToolTipStruct *sts = (SetToolTipStruct *)param;
 735     jobject self = sts->trayIcon;
 736     jstring jtooltip = sts->tooltip;
 737     AwtTrayIcon *trayIcon = NULL;
 738     LPCTSTR tooltipStr = NULL;
 739 
 740     PDATA pData;
 741     JNI_CHECK_PEER_GOTO(self, ret);
 742     trayIcon = (AwtTrayIcon *)pData;
 743 
 744     if (jtooltip == NULL) {
 745         trayIcon->SetToolTip(NULL);
 746         goto ret;
 747     }
 748 
 749     tooltipStr = JNU_GetStringPlatformChars(env, jtooltip, (jboolean *)NULL);
 750     if (env->ExceptionCheck()) goto ret;
 751     trayIcon->SetToolTip(tooltipStr);
 752     JNU_ReleaseStringPlatformChars(env, jtooltip, tooltipStr);
 753 ret:
 754     env->DeleteGlobalRef(self);
 755     env->DeleteGlobalRef(jtooltip);
 756     delete sts;
 757 }
 758 
 759 void AwtTrayIcon::SetIcon(HICON hIcon)
 760 {
 761     ::DestroyIcon(m_nid.hIcon);
 762     m_nid.hIcon = hIcon;
 763 }
 764 
 765 void AwtTrayIcon::_SetIcon(void *param)
 766 {
 767     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 768     SetIconStruct *sis = (SetIconStruct *)param;
 769     jobject self = sis->trayIcon;
 770     HICON hIcon = sis->hIcon;
 771     AwtTrayIcon *trayIcon = NULL;
 772 
 773     PDATA pData;
 774     JNI_CHECK_PEER_GOTO(self, ret);
 775     trayIcon = (AwtTrayIcon *)pData;
 776 
 777     trayIcon->SetIcon(hIcon);
 778 
 779 ret:
 780     env->DeleteGlobalRef(self);
 781     delete sis;
 782 }
 783 
 784 void AwtTrayIcon::_UpdateIcon(void *param)
 785 {
 786     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 787     UpdateIconStruct *uis = (UpdateIconStruct *)param;
 788     jobject self = uis->trayIcon;
 789     jboolean jupdate = uis->update;
 790     AwtTrayIcon *trayIcon = NULL;
 791 
 792     PDATA pData;
 793     JNI_CHECK_PEER_GOTO(self, ret);
 794     trayIcon = (AwtTrayIcon *)pData;
 795 
 796     BOOL result = trayIcon->SendTrayMessage(jupdate == JNI_TRUE ? NIM_MODIFY : NIM_ADD);
 797     // 6270114: Instructs the taskbar to behave according to the Shell version 5.0
 798     if (result && jupdate == JNI_FALSE) {
 799         trayIcon->SendTrayMessage(NIM_SETVERSION);
 800     }
 801 ret:
 802     env->DeleteGlobalRef(self);
 803     delete uis;
 804 }
 805 
 806 void AwtTrayIcon::DisplayMessage(LPCTSTR caption, LPCTSTR text, LPCTSTR msgType)
 807 {
 808     m_nid.uFlags |= NIF_INFO;
 809     m_nid.uTimeout = 10000;
 810 
 811     if (lstrcmp(msgType, TEXT("ERROR")) == 0) {
 812         m_nid.dwInfoFlags = NIIF_ERROR;
 813     } else if (lstrcmp(msgType, TEXT("WARNING")) == 0) {
 814         m_nid.dwInfoFlags = NIIF_WARNING;
 815     } else if (lstrcmp(msgType, TEXT("INFO")) == 0) {
 816         m_nid.dwInfoFlags = NIIF_INFO;
 817     } else if (lstrcmp(msgType, TEXT("NONE")) == 0) {
 818         m_nid.dwInfoFlags = NIIF_NONE;
 819     } else {
 820         m_nid.dwInfoFlags = NIIF_NONE;
 821     }
 822 
 823     if (caption[0] == '\0') {
 824         m_nid.szInfoTitle[0] = '\0';
 825 
 826     } else if (lstrlen(caption) >= TRAY_ICON_BALLOON_TITLE_MAX_SIZE) {
 827 
 828         _tcsncpy(m_nid.szInfoTitle, caption, TRAY_ICON_BALLOON_TITLE_MAX_SIZE);
 829         m_nid.szInfoTitle[TRAY_ICON_BALLOON_TITLE_MAX_SIZE - 1] = '\0';
 830 
 831     } else {
 832         _tcscpy_s(m_nid.szInfoTitle, TRAY_ICON_BALLOON_TITLE_MAX_SIZE, caption);
 833     }
 834 
 835     if (text[0] == '\0') {
 836         m_nid.szInfo[0] = ' ';
 837         m_nid.szInfo[1] = '\0';
 838 
 839     } else if (lstrlen(text) >= TRAY_ICON_BALLOON_INFO_MAX_SIZE) {
 840 
 841         _tcsncpy(m_nid.szInfo, text, TRAY_ICON_BALLOON_INFO_MAX_SIZE);
 842         m_nid.szInfo[TRAY_ICON_BALLOON_INFO_MAX_SIZE - 1] = '\0';
 843 
 844     } else {
 845         _tcscpy_s(m_nid.szInfo, TRAY_ICON_BALLOON_INFO_MAX_SIZE, text);
 846     }
 847 
 848     SendTrayMessage(NIM_MODIFY);
 849     m_nid.uFlags &= ~NIF_INFO;
 850 }
 851 
 852 void AwtTrayIcon::_DisplayMessage(void *param)
 853 {
 854     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 855     DisplayMessageStruct *dms = (DisplayMessageStruct *)param;
 856     jobject self = dms->trayIcon;
 857     jstring jcaption = dms->caption;
 858     jstring jtext = dms-> text;
 859     jstring jmsgType = dms->msgType;
 860     AwtTrayIcon *trayIcon = NULL;
 861     LPCTSTR captionStr = NULL;
 862     LPCTSTR textStr = NULL;
 863     LPCTSTR msgTypeStr = NULL;
 864 
 865     PDATA pData;
 866     JNI_CHECK_PEER_GOTO(self, ret);
 867     trayIcon = (AwtTrayIcon *)pData;
 868 
 869     captionStr = JNU_GetStringPlatformChars(env, jcaption, (jboolean *)NULL);
 870     if (env->ExceptionCheck()) goto ret;
 871     textStr = JNU_GetStringPlatformChars(env, jtext, (jboolean *)NULL);
 872     if (env->ExceptionCheck()) {
 873         JNU_ReleaseStringPlatformChars(env, jcaption, captionStr);
 874         goto ret;
 875     }
 876     msgTypeStr = JNU_GetStringPlatformChars(env, jmsgType, (jboolean *)NULL);
 877     if (env->ExceptionCheck()) {
 878         JNU_ReleaseStringPlatformChars(env, jcaption, captionStr);
 879         JNU_ReleaseStringPlatformChars(env, jtext, textStr);
 880         goto ret;
 881     }
 882     trayIcon->DisplayMessage(captionStr, textStr, msgTypeStr);
 883 
 884     JNU_ReleaseStringPlatformChars(env, jcaption, captionStr);
 885     JNU_ReleaseStringPlatformChars(env, jtext, textStr);
 886     JNU_ReleaseStringPlatformChars(env, jmsgType, msgTypeStr);
 887 ret:
 888     env->DeleteGlobalRef(self);
 889     env->DeleteGlobalRef(jcaption);
 890     env->DeleteGlobalRef(jtext);
 891     env->DeleteGlobalRef(jmsgType);
 892     delete dms;
 893 }
 894 
 895 /************************************************************************
 896  * TrayIcon native methods
 897  */
 898 
 899 extern "C" {
 900 
 901 /*
 902  * Class:     java_awt_TrayIcon
 903  * Method:    initIDs
 904  * Signature: ()V
 905  */
 906 JNIEXPORT void JNICALL
 907 Java_java_awt_TrayIcon_initIDs(JNIEnv *env, jclass cls)
 908 {
 909     TRY;
 910 
 911     /* init field ids */
 912     AwtTrayIcon::idID = env->GetFieldID(cls, "id", "I");
 913     DASSERT(AwtTrayIcon::idID != NULL);
 914     CHECK_NULL(AwtTrayIcon::idID);
 915 
 916     AwtTrayIcon::actionCommandID = env->GetFieldID(cls, "actionCommand", "Ljava/lang/String;");
 917     DASSERT(AwtTrayIcon::actionCommandID != NULL);
 918     CHECK_NULL( AwtTrayIcon::actionCommandID);
 919 
 920     CATCH_BAD_ALLOC;
 921 }
 922 
 923 /*
 924  * Class:     sun_awt_windows_WTrayIconPeer
 925  * Method:    create
 926  * Signature: ()V
 927  */
 928 JNIEXPORT void JNICALL
 929 Java_sun_awt_windows_WTrayIconPeer_create(JNIEnv *env, jobject self)
 930 {
 931     TRY;
 932 
 933     AwtToolkit::CreateComponent(self, NULL,
 934                                 (AwtToolkit::ComponentFactory)
 935                                 AwtTrayIcon::Create);
 936     PDATA pData;
 937     JNI_CHECK_PEER_CREATION_RETURN(self);
 938 
 939     CATCH_BAD_ALLOC;
 940 }
 941 
 942 /*
 943  * Class:     sun_awt_windows_WTrayIconPeer
 944  * Method:    _dispose
 945  * Signature: ()V
 946  */
 947 JNIEXPORT void JNICALL
 948 Java_sun_awt_windows_WTrayIconPeer__1dispose(JNIEnv *env, jobject self)
 949 {
 950     TRY;
 951 
 952     AwtObject::_Dispose(self);
 953 
 954     CATCH_BAD_ALLOC;
 955 }
 956 
 957 /*
 958  * Class:     sun_awt_windows_WTrayIconPeer
 959  * Method:    _setToolTip
 960  * Signature: ()V
 961  */
 962 JNIEXPORT void JNICALL
 963 Java_sun_awt_windows_WTrayIconPeer_setToolTip(JNIEnv *env, jobject self,
 964                                               jstring tooltip)
 965 {
 966     TRY;
 967 
 968     SetToolTipStruct *sts = new SetToolTipStruct;
 969     sts->trayIcon = env->NewGlobalRef(self);
 970     if (tooltip != NULL) {
 971         sts->tooltip = (jstring)env->NewGlobalRef(tooltip);
 972     } else {
 973         sts->tooltip = NULL;
 974     }
 975 
 976     AwtToolkit::GetInstance().SyncCall(AwtTrayIcon::_SetToolTip, sts);
 977     // global ref and sts are deleted in _SetToolTip
 978 
 979     CATCH_BAD_ALLOC;
 980 }
 981 
 982 /*
 983  * Class:     sun_awt_windows_WTrayIconPeer
 984  * Method:    setNativeIcon
 985  * Signature: (I[B[IIIII)V
 986  */
 987 JNIEXPORT void JNICALL
 988 Java_sun_awt_windows_WTrayIconPeer_setNativeIcon(JNIEnv *env, jobject self,
 989                                                  jintArray intRasterData, jbyteArray andMask,
 990                                                  jint nSS, jint nW, jint nH)
 991 {
 992     TRY;
 993 
 994     int length = env->GetArrayLength(andMask);
 995     jbyte *andMaskPtr = new jbyte[length];
 996 
 997     env->GetByteArrayRegion(andMask, 0, length, andMaskPtr);
 998 
 999     HBITMAP hMask = ::CreateBitmap(nW, nH, 1, 1, (BYTE *)andMaskPtr);
1000 //    ::GdiFlush();
1001 
1002     delete[] andMaskPtr;
1003 
1004     jint *intRasterDataPtr = NULL;
1005     HBITMAP hColor = NULL;
1006     try {
1007         intRasterDataPtr = (jint *)env->GetPrimitiveArrayCritical(intRasterData, 0);
1008         if (intRasterDataPtr == NULL) {
1009             ::DeleteObject(hMask);
1010             return;
1011         }
1012         hColor = AwtTrayIcon::CreateBMP(NULL, (int *)intRasterDataPtr, nSS, nW, nH);
1013     } catch (...) {
1014         if (intRasterDataPtr != NULL) {
1015             env->ReleasePrimitiveArrayCritical(intRasterData, intRasterDataPtr, 0);
1016         }
1017         ::DeleteObject(hMask);
1018         throw;
1019     }
1020 
1021     env->ReleasePrimitiveArrayCritical(intRasterData, intRasterDataPtr, 0);
1022     intRasterDataPtr = NULL;
1023 
1024     HICON hIcon = NULL;
1025 
1026     if (hMask && hColor) {
1027         ICONINFO icnInfo;
1028         memset(&icnInfo, 0, sizeof(ICONINFO));
1029         icnInfo.hbmMask = hMask;
1030         icnInfo.hbmColor = hColor;
1031         icnInfo.fIcon = TRUE;
1032         icnInfo.xHotspot = TRAY_ICON_X_HOTSPOT;
1033         icnInfo.yHotspot = TRAY_ICON_Y_HOTSPOT;
1034 
1035         hIcon = ::CreateIconIndirect(&icnInfo);
1036     }
1037     ::DeleteObject(hColor);
1038     ::DeleteObject(hMask);
1039 
1040     //////////////////////////////////////////
1041 
1042     SetIconStruct *sis = new SetIconStruct;
1043     sis->trayIcon = env->NewGlobalRef(self);
1044     sis->hIcon = hIcon;
1045 
1046     AwtToolkit::GetInstance().SyncCall(AwtTrayIcon::_SetIcon, sis);
1047     // global ref is deleted in _SetIcon
1048 
1049     CATCH_BAD_ALLOC;
1050 }
1051 
1052 /*
1053  * Class:     sun_awt_windows_WTrayIconPeer
1054  * Method:    updateNativeIcon
1055  * Signature: (Z)V
1056  */
1057 JNIEXPORT void JNICALL
1058 Java_sun_awt_windows_WTrayIconPeer_updateNativeIcon(JNIEnv *env, jobject self,
1059                                                     jboolean doUpdate)
1060 {
1061     TRY;
1062 
1063     UpdateIconStruct *uis = new UpdateIconStruct;
1064     uis->trayIcon = env->NewGlobalRef(self);
1065     uis->update = doUpdate;
1066 
1067     AwtToolkit::GetInstance().SyncCall(AwtTrayIcon::_UpdateIcon, uis);
1068     // global ref is deleted in _UpdateIcon
1069 
1070     CATCH_BAD_ALLOC;
1071 }
1072 
1073 /*
1074  * Class:     sun_awt_windows_WTrayIconPeer
1075  * Method:    displayMessage
1076  * Signature: ()V;
1077  */
1078 JNIEXPORT void JNICALL
1079 Java_sun_awt_windows_WTrayIconPeer__1displayMessage(JNIEnv *env, jobject self,
1080     jstring caption, jstring text, jstring msgType)
1081 {
1082     TRY;
1083 
1084     DisplayMessageStruct *dms = new DisplayMessageStruct;
1085     dms->trayIcon = env->NewGlobalRef(self);
1086     dms->caption = (jstring)env->NewGlobalRef(caption);
1087     dms->text = (jstring)env->NewGlobalRef(text);
1088     dms->msgType = (jstring)env->NewGlobalRef(msgType);
1089 
1090     AwtToolkit::GetInstance().SyncCall(AwtTrayIcon::_DisplayMessage, dms);
1091     // global ref is deleted in _DisplayMessage
1092 
1093     CATCH_BAD_ALLOC(NULL);
1094 }
1095 
1096 } /* extern "C" */