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