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" */