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