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     if (AwtComponent::GetJavaModifiers() & java_awt_event_InputEvent_BUTTON1_DOWN_MASK) {
 415         MSG msg;
 416         AwtComponent::InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y);
 417         SendActionEvent(java_awt_event_ActionEvent_ACTION_PERFORMED, ::JVM_CurrentTimeMillis(NULL, 0),
 418                         AwtComponent::GetActionModifiers(), &msg);
 419     }
 420     return mrConsume;
 421 }
 422 
 423 MsgRouting AwtTrayIcon::WmKeySelect(UINT flags, int x, int y)
 424 {
 425     static jlong lastKeySelectTime = 0;
 426     jlong now = ::JVM_CurrentTimeMillis(NULL, 0);
 427 
 428     // If a user selects a notify icon with the ENTER key,
 429     // Shell 5.0 sends double NIN_KEYSELECT notification.
 430     if (lastKeySelectTime != now) {
 431         MSG msg;
 432         AwtComponent::InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y);
 433         SendActionEvent(java_awt_event_ActionEvent_ACTION_PERFORMED, ::JVM_CurrentTimeMillis(NULL, 0),
 434                         AwtComponent::GetActionModifiers(), &msg);
 435     }
 436     lastKeySelectTime = now;
 437 
 438     return mrConsume;
 439 }
 440 
 441 MsgRouting AwtTrayIcon::WmSelect(UINT flags, int x, int y)
 442 {
 443 
 444     // If a user click on a notify icon with the mouse,
 445     // Shell 5.0 sends NIN_SELECT notification on every click.
 446     // To be compatible with JDK6.0 only second click is important.
 447     if (clickCount == 2) {
 448         MSG msg;
 449         AwtComponent::InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y);
 450         SendActionEvent(java_awt_event_ActionEvent_ACTION_PERFORMED, ::JVM_CurrentTimeMillis(NULL, 0),
 451                         AwtComponent::GetActionModifiers(), &msg);
 452     }
 453     return mrConsume;
 454 }
 455 
 456 MsgRouting AwtTrayIcon::WmContextMenu(UINT flags, int x, int y)
 457 {
 458     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 459     jobject peer = GetPeer(env);
 460     if (peer != NULL) {
 461         JNU_CallMethodByName(env, NULL, peer, "showPopupMenu",
 462                              "(II)V", x, y);
 463     }
 464     return mrConsume;
 465 }
 466 
 467 /*
 468  * Adds all icons we already have to taskbar.
 469  * We use this method on taskbar recreation (see 6369062).
 470  */
 471 MsgRouting AwtTrayIcon::WmTaskbarCreated() {
 472     TrayIconListItem* item;
 473     for (item = sm_trayIconList; item != NULL; item = item->m_next) {
 474         BOOL result = item->m_trayIcon->SendTrayMessage(NIM_ADD);
 475         // 6270114: Instructs the taskbar to behave according to the Shell version 5.0
 476         if (result) {
 477             item->m_trayIcon->SendTrayMessage(NIM_SETVERSION);
 478         }
 479     }
 480     return mrDoDefault;
 481 }
 482 
 483 void AwtTrayIcon::SendMouseEvent(jint id, jlong when, jint x, jint y,
 484                                  jint modifiers, jint clickCount,
 485                                  jboolean popupTrigger, jint button,
 486                                  MSG *pMsg)
 487 {
 488     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 489     if (GetPeer(env) == NULL) {
 490         /* event received during termination. */
 491         return;
 492     }
 493 
 494     static jclass mouseEventCls;
 495     if (mouseEventCls == NULL) {
 496         jclass mouseEventClsLocal =
 497             env->FindClass("java/awt/event/MouseEvent");
 498         if (!mouseEventClsLocal) {
 499             /* exception already thrown */
 500             return;
 501         }
 502         mouseEventCls = (jclass)env->NewGlobalRef(mouseEventClsLocal);
 503         env->DeleteLocalRef(mouseEventClsLocal);
 504     }
 505 
 506     static jmethodID mouseEventConst;
 507     if (mouseEventConst == NULL) {
 508         mouseEventConst =
 509             env->GetMethodID(mouseEventCls, "<init>",
 510                              "(Ljava/awt/Component;IJIIIIIIZI)V");
 511         DASSERT(mouseEventConst);
 512         CHECK_NULL(mouseEventConst);
 513     }
 514     if (env->EnsureLocalCapacity(2) < 0) {
 515         return;
 516     }
 517     jobject target = GetTarget(env);
 518     jobject mouseEvent = env->NewObject(mouseEventCls, mouseEventConst,
 519                                         target,
 520                                         id, when, modifiers,
 521                                         x, y, // no client area coordinates
 522                                         x, y,
 523                                         clickCount, popupTrigger, button);
 524 
 525     if (safe_ExceptionOccurred(env)) {
 526         env->ExceptionDescribe();
 527         env->ExceptionClear();
 528     }
 529 
 530     DASSERT(mouseEvent != NULL);
 531     if (pMsg != 0) {
 532         AwtAWTEvent::saveMSG(env, pMsg, mouseEvent);
 533     }
 534     SendEvent(mouseEvent);
 535 
 536     env->DeleteLocalRef(mouseEvent);
 537     env->DeleteLocalRef(target);
 538 }
 539 
 540 void AwtTrayIcon::SendActionEvent(jint id, jlong when, jint modifiers, MSG *pMsg)
 541 {
 542     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 543     if (GetPeer(env) == NULL) {
 544         /* event received during termination. */
 545         return;
 546     }
 547 
 548     static jclass actionEventCls;
 549     if (actionEventCls == NULL) {
 550         jclass actionEventClsLocal =
 551             env->FindClass("java/awt/event/ActionEvent");
 552         if (!actionEventClsLocal) {
 553             /* exception already thrown */
 554             return;
 555         }
 556         actionEventCls = (jclass)env->NewGlobalRef(actionEventClsLocal);
 557         env->DeleteLocalRef(actionEventClsLocal);
 558     }
 559 
 560     static jmethodID actionEventConst;
 561     if (actionEventConst == NULL) {
 562         actionEventConst =
 563             env->GetMethodID(actionEventCls, "<init>",
 564                              "(Ljava/lang/Object;ILjava/lang/String;JI)V");
 565         DASSERT(actionEventConst);
 566         CHECK_NULL(actionEventConst);
 567     }
 568     if (env->EnsureLocalCapacity(2) < 0) {
 569         return;
 570     }
 571     jobject target = GetTarget(env);
 572     jstring actionCommand = (jstring)env->GetObjectField(target, AwtTrayIcon::actionCommandID);
 573     jobject actionEvent = env->NewObject(actionEventCls, actionEventConst,
 574                                          target, id, actionCommand, when, modifiers);
 575 
 576     if (safe_ExceptionOccurred(env)) {
 577         env->ExceptionDescribe();
 578         env->ExceptionClear();
 579     }
 580 
 581     DASSERT(actionEvent != NULL);
 582     if (pMsg != 0) {
 583         AwtAWTEvent::saveMSG(env, pMsg, actionEvent);
 584     }
 585     SendEvent(actionEvent);
 586 
 587     env->DeleteLocalRef(actionEvent);
 588     env->DeleteLocalRef(target);
 589     env->DeleteLocalRef(actionCommand);
 590 }
 591 
 592 AwtTrayIcon* AwtTrayIcon::SearchTrayIconItem(UINT id) {
 593     TrayIconListItem* item;
 594     for (item = sm_trayIconList; item != NULL; item = item->m_next) {
 595         if (item->m_ID == id) {
 596             return item->m_trayIcon;
 597         }
 598     }
 599     /*
 600      * DASSERT(FALSE);
 601      * This should not be happend if all tray icons are recorded
 602      */
 603     return NULL;
 604 }
 605 
 606 void AwtTrayIcon::RemoveTrayIconItem(UINT id) {
 607     TrayIconListItem* item = sm_trayIconList;
 608     TrayIconListItem* lastItem = NULL;
 609     while (item != NULL) {
 610         if (item->m_ID == id) {
 611             if (lastItem == NULL) {
 612                 sm_trayIconList = item->m_next;
 613             } else {
 614                 lastItem->m_next = item->m_next;
 615             }
 616             item->m_next = NULL;
 617             DASSERT(item != NULL);
 618             delete item;
 619             return;
 620         }
 621         lastItem = item;
 622         item = item->m_next;
 623     }
 624 }
 625 
 626 void AwtTrayIcon::LinkObjects(JNIEnv *env, jobject peer)
 627 {
 628     if (m_peerObject == NULL) {
 629         m_peerObject = env->NewGlobalRef(peer);
 630     }
 631 
 632     /* Bind JavaPeer -> C++*/
 633     JNI_SET_PDATA(peer, this);
 634 }
 635 
 636 void AwtTrayIcon::UnlinkObjects()
 637 {
 638     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 639     if (m_peerObject) {
 640         JNI_SET_PDATA(m_peerObject, static_cast<PDATA>(NULL));
 641         env->DeleteGlobalRef(m_peerObject);
 642         m_peerObject = NULL;
 643     }
 644 }
 645 
 646 HBITMAP AwtTrayIcon::CreateBMP(HWND hW,int* imageData,int nSS, int nW, int nH)
 647 {
 648     Bitmapheader    bmhHeader = {0};
 649     HDC             hDC;
 650     char            *ptrImageData;
 651     HBITMAP         hbmpBitmap;
 652     HBITMAP         hBitmap;
 653     int             nNumChannels    = 4;
 654 
 655     if (!hW) {
 656         hW = ::GetDesktopWindow();
 657     }
 658     hDC = ::GetDC(hW);
 659     if (!hDC) {
 660         return NULL;
 661     }
 662 
 663     bmhHeader.bmiHeader.bV5Size              = sizeof(BITMAPV5HEADER);
 664     bmhHeader.bmiHeader.bV5Width             = nW;
 665     bmhHeader.bmiHeader.bV5Height            = -nH;
 666     bmhHeader.bmiHeader.bV5Planes            = 1;
 667 
 668     bmhHeader.bmiHeader.bV5BitCount          = 32;
 669     bmhHeader.bmiHeader.bV5Compression       = BI_BITFIELDS;
 670 
 671     // The following mask specification specifies a supported 32 BPP
 672     // alpha format for Windows XP.
 673     bmhHeader.bmiHeader.bV5RedMask   =  0x00FF0000;
 674     bmhHeader.bmiHeader.bV5GreenMask =  0x0000FF00;
 675     bmhHeader.bmiHeader.bV5BlueMask  =  0x000000FF;
 676     bmhHeader.bmiHeader.bV5AlphaMask =  0xFF000000;
 677 
 678     hbmpBitmap = ::CreateDIBSection(hDC, (BITMAPINFO*)&(bmhHeader),
 679                                     DIB_RGB_COLORS,
 680                                     (void**)&(ptrImageData),
 681                                     NULL, 0);
 682     int  *srcPtr = imageData;
 683     char *dstPtr = ptrImageData;
 684     if (!dstPtr) {
 685         ReleaseDC(hW, hDC);
 686         return NULL;
 687     }
 688     for (int nOutern = 0; nOutern < nH; nOutern++) {
 689         for (int nInner = 0; nInner < nSS; nInner++) {
 690             dstPtr[3] = (*srcPtr >> 0x18) & 0xFF;
 691             dstPtr[2] = (*srcPtr >> 0x10) & 0xFF;
 692             dstPtr[1] = (*srcPtr >> 0x08) & 0xFF;
 693             dstPtr[0] = *srcPtr & 0xFF;
 694 
 695             srcPtr++;
 696             dstPtr += nNumChannels;
 697         }
 698     }
 699 
 700     // convert it into DDB to make CustomCursor work on WIN95
 701     hBitmap = CreateDIBitmap(hDC,
 702                              (BITMAPINFOHEADER*)&bmhHeader,
 703                              CBM_INIT,
 704                              (void *)ptrImageData,
 705                              (BITMAPINFO*)&bmhHeader,
 706                              DIB_RGB_COLORS);
 707 
 708     ::DeleteObject(hbmpBitmap);
 709     ::ReleaseDC(hW, hDC);
 710 //  ::GdiFlush();
 711     return hBitmap;
 712 }
 713 
 714 void AwtTrayIcon::SetToolTip(LPCTSTR tooltip)
 715 {
 716     if (tooltip == NULL) {
 717         m_nid.szTip[0] = '\0';
 718     } else if (lstrlen(tooltip) >= TRAY_ICON_TOOLTIP_MAX_SIZE) {
 719         _tcsncpy(m_nid.szTip, tooltip, TRAY_ICON_TOOLTIP_MAX_SIZE);
 720         m_nid.szTip[TRAY_ICON_TOOLTIP_MAX_SIZE - 1] = '\0';
 721     } else {
 722         _tcscpy_s(m_nid.szTip, TRAY_ICON_TOOLTIP_MAX_SIZE, tooltip);
 723     }
 724 
 725     SendTrayMessage(NIM_MODIFY);
 726 }
 727 
 728 void AwtTrayIcon::_SetToolTip(void *param)
 729 {
 730     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 731     SetToolTipStruct *sts = (SetToolTipStruct *)param;
 732     jobject self = sts->trayIcon;
 733     jstring jtooltip = sts->tooltip;
 734     AwtTrayIcon *trayIcon = NULL;
 735     LPCTSTR tooltipStr = NULL;
 736 
 737     PDATA pData;
 738     JNI_CHECK_PEER_GOTO(self, ret);
 739     trayIcon = (AwtTrayIcon *)pData;
 740 
 741     if (jtooltip == NULL) {
 742         trayIcon->SetToolTip(NULL);
 743         goto ret;
 744     }
 745 
 746     tooltipStr = JNU_GetStringPlatformChars(env, jtooltip, (jboolean *)NULL);
 747     if (env->ExceptionCheck()) goto ret;
 748     trayIcon->SetToolTip(tooltipStr);
 749     JNU_ReleaseStringPlatformChars(env, jtooltip, tooltipStr);
 750 ret:
 751     env->DeleteGlobalRef(self);
 752     env->DeleteGlobalRef(jtooltip);
 753     delete sts;
 754 }
 755 
 756 void AwtTrayIcon::SetIcon(HICON hIcon)
 757 {
 758     ::DestroyIcon(m_nid.hIcon);
 759     m_nid.hIcon = hIcon;
 760 }
 761 
 762 void AwtTrayIcon::_SetIcon(void *param)
 763 {
 764     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 765     SetIconStruct *sis = (SetIconStruct *)param;
 766     jobject self = sis->trayIcon;
 767     HICON hIcon = sis->hIcon;
 768     AwtTrayIcon *trayIcon = NULL;
 769 
 770     PDATA pData;
 771     JNI_CHECK_PEER_GOTO(self, ret);
 772     trayIcon = (AwtTrayIcon *)pData;
 773 
 774     trayIcon->SetIcon(hIcon);
 775 
 776 ret:
 777     env->DeleteGlobalRef(self);
 778     delete sis;
 779 }
 780 
 781 void AwtTrayIcon::_UpdateIcon(void *param)
 782 {
 783     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 784     UpdateIconStruct *uis = (UpdateIconStruct *)param;
 785     jobject self = uis->trayIcon;
 786     jboolean jupdate = uis->update;
 787     AwtTrayIcon *trayIcon = NULL;
 788 
 789     PDATA pData;
 790     JNI_CHECK_PEER_GOTO(self, ret);
 791     trayIcon = (AwtTrayIcon *)pData;
 792 
 793     BOOL result = trayIcon->SendTrayMessage(jupdate == JNI_TRUE ? NIM_MODIFY : NIM_ADD);
 794     // 6270114: Instructs the taskbar to behave according to the Shell version 5.0
 795     if (result && jupdate == JNI_FALSE) {
 796         trayIcon->SendTrayMessage(NIM_SETVERSION);
 797     }
 798 ret:
 799     env->DeleteGlobalRef(self);
 800     delete uis;
 801 }
 802 
 803 void AwtTrayIcon::DisplayMessage(LPCTSTR caption, LPCTSTR text, LPCTSTR msgType)
 804 {
 805     m_nid.uFlags |= NIF_INFO;
 806     m_nid.uTimeout = 10000;
 807 
 808     if (lstrcmp(msgType, TEXT("ERROR")) == 0) {
 809         m_nid.dwInfoFlags = NIIF_ERROR;
 810     } else if (lstrcmp(msgType, TEXT("WARNING")) == 0) {
 811         m_nid.dwInfoFlags = NIIF_WARNING;
 812     } else if (lstrcmp(msgType, TEXT("INFO")) == 0) {
 813         m_nid.dwInfoFlags = NIIF_INFO;
 814     } else if (lstrcmp(msgType, TEXT("NONE")) == 0) {
 815         m_nid.dwInfoFlags = NIIF_NONE;
 816     } else {
 817         m_nid.dwInfoFlags = NIIF_NONE;
 818     }
 819 
 820     if (caption[0] == '\0') {
 821         m_nid.szInfoTitle[0] = '\0';
 822 
 823     } else if (lstrlen(caption) >= TRAY_ICON_BALLOON_TITLE_MAX_SIZE) {
 824 
 825         _tcsncpy(m_nid.szInfoTitle, caption, TRAY_ICON_BALLOON_TITLE_MAX_SIZE);
 826         m_nid.szInfoTitle[TRAY_ICON_BALLOON_TITLE_MAX_SIZE - 1] = '\0';
 827 
 828     } else {
 829         _tcscpy_s(m_nid.szInfoTitle, TRAY_ICON_BALLOON_TITLE_MAX_SIZE, caption);
 830     }
 831 
 832     if (text[0] == '\0') {
 833         m_nid.szInfo[0] = ' ';
 834         m_nid.szInfo[1] = '\0';
 835 
 836     } else if (lstrlen(text) >= TRAY_ICON_BALLOON_INFO_MAX_SIZE) {
 837 
 838         _tcsncpy(m_nid.szInfo, text, TRAY_ICON_BALLOON_INFO_MAX_SIZE);
 839         m_nid.szInfo[TRAY_ICON_BALLOON_INFO_MAX_SIZE - 1] = '\0';
 840 
 841     } else {
 842         _tcscpy_s(m_nid.szInfo, TRAY_ICON_BALLOON_INFO_MAX_SIZE, text);
 843     }
 844 
 845     SendTrayMessage(NIM_MODIFY);
 846     m_nid.uFlags &= ~NIF_INFO;
 847 }
 848 
 849 void AwtTrayIcon::_DisplayMessage(void *param)
 850 {
 851     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 852     DisplayMessageStruct *dms = (DisplayMessageStruct *)param;
 853     jobject self = dms->trayIcon;
 854     jstring jcaption = dms->caption;
 855     jstring jtext = dms-> text;
 856     jstring jmsgType = dms->msgType;
 857     AwtTrayIcon *trayIcon = NULL;
 858     LPCTSTR captionStr = NULL;
 859     LPCTSTR textStr = NULL;
 860     LPCTSTR msgTypeStr = NULL;
 861 
 862     PDATA pData;
 863     JNI_CHECK_PEER_GOTO(self, ret);
 864     trayIcon = (AwtTrayIcon *)pData;
 865 
 866     captionStr = JNU_GetStringPlatformChars(env, jcaption, (jboolean *)NULL);
 867     if (env->ExceptionCheck()) goto ret;
 868     textStr = JNU_GetStringPlatformChars(env, jtext, (jboolean *)NULL);
 869     if (env->ExceptionCheck()) {
 870         JNU_ReleaseStringPlatformChars(env, jcaption, captionStr);
 871         goto ret;
 872     }
 873     msgTypeStr = JNU_GetStringPlatformChars(env, jmsgType, (jboolean *)NULL);
 874     if (env->ExceptionCheck()) {
 875         JNU_ReleaseStringPlatformChars(env, jcaption, captionStr);
 876         JNU_ReleaseStringPlatformChars(env, jtext, textStr);
 877         goto ret;
 878     }
 879     trayIcon->DisplayMessage(captionStr, textStr, msgTypeStr);
 880 
 881     JNU_ReleaseStringPlatformChars(env, jcaption, captionStr);
 882     JNU_ReleaseStringPlatformChars(env, jtext, textStr);
 883     JNU_ReleaseStringPlatformChars(env, jmsgType, msgTypeStr);
 884 ret:
 885     env->DeleteGlobalRef(self);
 886     env->DeleteGlobalRef(jcaption);
 887     env->DeleteGlobalRef(jtext);
 888     env->DeleteGlobalRef(jmsgType);
 889     delete dms;
 890 }
 891 
 892 /************************************************************************
 893  * TrayIcon native methods
 894  */
 895 
 896 extern "C" {
 897 
 898 /*
 899  * Class:     java_awt_TrayIcon
 900  * Method:    initIDs
 901  * Signature: ()V
 902  */
 903 JNIEXPORT void JNICALL
 904 Java_java_awt_TrayIcon_initIDs(JNIEnv *env, jclass cls)
 905 {
 906     TRY;
 907 
 908     /* init field ids */
 909     AwtTrayIcon::idID = env->GetFieldID(cls, "id", "I");
 910     DASSERT(AwtTrayIcon::idID != NULL);
 911     CHECK_NULL(AwtTrayIcon::idID);
 912 
 913     AwtTrayIcon::actionCommandID = env->GetFieldID(cls, "actionCommand", "Ljava/lang/String;");
 914     DASSERT(AwtTrayIcon::actionCommandID != NULL);
 915     CHECK_NULL( AwtTrayIcon::actionCommandID);
 916 
 917     CATCH_BAD_ALLOC;
 918 }
 919 
 920 /*
 921  * Class:     sun_awt_windows_WTrayIconPeer
 922  * Method:    create
 923  * Signature: ()V
 924  */
 925 JNIEXPORT void JNICALL
 926 Java_sun_awt_windows_WTrayIconPeer_create(JNIEnv *env, jobject self)
 927 {
 928     TRY;
 929 
 930     AwtToolkit::CreateComponent(self, NULL,
 931                                 (AwtToolkit::ComponentFactory)
 932                                 AwtTrayIcon::Create);
 933     PDATA pData;
 934     JNI_CHECK_PEER_CREATION_RETURN(self);
 935 
 936     CATCH_BAD_ALLOC;
 937 }
 938 
 939 /*
 940  * Class:     sun_awt_windows_WTrayIconPeer
 941  * Method:    _dispose
 942  * Signature: ()V
 943  */
 944 JNIEXPORT void JNICALL
 945 Java_sun_awt_windows_WTrayIconPeer__1dispose(JNIEnv *env, jobject self)
 946 {
 947     TRY;
 948 
 949     AwtObject::_Dispose(self);
 950 
 951     CATCH_BAD_ALLOC;
 952 }
 953 
 954 /*
 955  * Class:     sun_awt_windows_WTrayIconPeer
 956  * Method:    _setToolTip
 957  * Signature: ()V
 958  */
 959 JNIEXPORT void JNICALL
 960 Java_sun_awt_windows_WTrayIconPeer_setToolTip(JNIEnv *env, jobject self,
 961                                               jstring tooltip)
 962 {
 963     TRY;
 964 
 965     SetToolTipStruct *sts = new SetToolTipStruct;
 966     sts->trayIcon = env->NewGlobalRef(self);
 967     if (tooltip != NULL) {
 968         sts->tooltip = (jstring)env->NewGlobalRef(tooltip);
 969     } else {
 970         sts->tooltip = NULL;
 971     }
 972 
 973     AwtToolkit::GetInstance().SyncCall(AwtTrayIcon::_SetToolTip, sts);
 974     // global ref and sts are deleted in _SetToolTip
 975 
 976     CATCH_BAD_ALLOC;
 977 }
 978 
 979 /*
 980  * Class:     sun_awt_windows_WTrayIconPeer
 981  * Method:    setNativeIcon
 982  * Signature: (I[B[IIIII)V
 983  */
 984 JNIEXPORT void JNICALL
 985 Java_sun_awt_windows_WTrayIconPeer_setNativeIcon(JNIEnv *env, jobject self,
 986                                                  jintArray intRasterData, jbyteArray andMask,
 987                                                  jint nSS, jint nW, jint nH)
 988 {
 989     TRY;
 990 
 991     int length = env->GetArrayLength(andMask);
 992     jbyte *andMaskPtr = new jbyte[length];
 993 
 994     env->GetByteArrayRegion(andMask, 0, length, andMaskPtr);
 995 
 996     HBITMAP hMask = ::CreateBitmap(nW, nH, 1, 1, (BYTE *)andMaskPtr);
 997 //    ::GdiFlush();
 998 
 999     delete[] andMaskPtr;
1000 
1001     jint *intRasterDataPtr = NULL;
1002     HBITMAP hColor = NULL;
1003     try {
1004         intRasterDataPtr = (jint *)env->GetPrimitiveArrayCritical(intRasterData, 0);
1005         if (intRasterDataPtr == NULL) {
1006             ::DeleteObject(hMask);
1007             return;
1008         }
1009         hColor = AwtTrayIcon::CreateBMP(NULL, (int *)intRasterDataPtr, nSS, nW, nH);
1010     } catch (...) {
1011         if (intRasterDataPtr != NULL) {
1012             env->ReleasePrimitiveArrayCritical(intRasterData, intRasterDataPtr, 0);
1013         }
1014         ::DeleteObject(hMask);
1015         throw;
1016     }
1017 
1018     env->ReleasePrimitiveArrayCritical(intRasterData, intRasterDataPtr, 0);
1019     intRasterDataPtr = NULL;
1020 
1021     HICON hIcon = NULL;
1022 
1023     if (hMask && hColor) {
1024         ICONINFO icnInfo;
1025         memset(&icnInfo, 0, sizeof(ICONINFO));
1026         icnInfo.hbmMask = hMask;
1027         icnInfo.hbmColor = hColor;
1028         icnInfo.fIcon = TRUE;
1029         icnInfo.xHotspot = TRAY_ICON_X_HOTSPOT;
1030         icnInfo.yHotspot = TRAY_ICON_Y_HOTSPOT;
1031 
1032         hIcon = ::CreateIconIndirect(&icnInfo);
1033     }
1034     ::DeleteObject(hColor);
1035     ::DeleteObject(hMask);
1036 
1037     //////////////////////////////////////////
1038 
1039     SetIconStruct *sis = new SetIconStruct;
1040     sis->trayIcon = env->NewGlobalRef(self);
1041     sis->hIcon = hIcon;
1042 
1043     AwtToolkit::GetInstance().SyncCall(AwtTrayIcon::_SetIcon, sis);
1044     // global ref is deleted in _SetIcon
1045 
1046     CATCH_BAD_ALLOC;
1047 }
1048 
1049 /*
1050  * Class:     sun_awt_windows_WTrayIconPeer
1051  * Method:    updateNativeIcon
1052  * Signature: (Z)V
1053  */
1054 JNIEXPORT void JNICALL
1055 Java_sun_awt_windows_WTrayIconPeer_updateNativeIcon(JNIEnv *env, jobject self,
1056                                                     jboolean doUpdate)
1057 {
1058     TRY;
1059 
1060     UpdateIconStruct *uis = new UpdateIconStruct;
1061     uis->trayIcon = env->NewGlobalRef(self);
1062     uis->update = doUpdate;
1063 
1064     AwtToolkit::GetInstance().SyncCall(AwtTrayIcon::_UpdateIcon, uis);
1065     // global ref is deleted in _UpdateIcon
1066 
1067     CATCH_BAD_ALLOC;
1068 }
1069 
1070 /*
1071  * Class:     sun_awt_windows_WTrayIconPeer
1072  * Method:    displayMessage
1073  * Signature: ()V;
1074  */
1075 JNIEXPORT void JNICALL
1076 Java_sun_awt_windows_WTrayIconPeer__1displayMessage(JNIEnv *env, jobject self,
1077     jstring caption, jstring text, jstring msgType)
1078 {
1079     TRY;
1080 
1081     DisplayMessageStruct *dms = new DisplayMessageStruct;
1082     dms->trayIcon = env->NewGlobalRef(self);
1083     dms->caption = (jstring)env->NewGlobalRef(caption);
1084     dms->text = (jstring)env->NewGlobalRef(text);
1085     dms->msgType = (jstring)env->NewGlobalRef(msgType);
1086 
1087     AwtToolkit::GetInstance().SyncCall(AwtTrayIcon::_DisplayMessage, dms);
1088     // global ref is deleted in _DisplayMessage
1089 
1090     CATCH_BAD_ALLOC(NULL);
1091 }
1092 
1093 } /* extern "C" */