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